初始化项目 #1
@ -2,17 +2,31 @@ import request from '@/utils/request'
|
||||
|
||||
// 查询资产分类列表
|
||||
export function listClassification(query) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
const params = {}
|
||||
if (query) {
|
||||
Object.keys(query).forEach(key => {
|
||||
if (key === 'classificationCode') {
|
||||
params.classification_code = query[key]
|
||||
} else if (key === 'classificationName') {
|
||||
params.classification_name = query[key]
|
||||
} else {
|
||||
params[key] = query[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return request({
|
||||
url: '/asset/classification/list',
|
||||
url: '/asset/classifications/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 查询资产分类详情
|
||||
export function getClassification(id) {
|
||||
return request({
|
||||
url: '/asset/classification/' + id,
|
||||
url: '/asset/classifications/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@ -20,7 +34,7 @@ export function getClassification(id) {
|
||||
// 新增资产分类
|
||||
export function addClassification(data) {
|
||||
return request({
|
||||
url: '/asset/classification',
|
||||
url: '/asset/classifications/create',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
@ -29,68 +43,122 @@ export function addClassification(data) {
|
||||
// 修改资产分类
|
||||
export function updateClassification(data) {
|
||||
return request({
|
||||
url: '/asset/classification',
|
||||
url: '/asset/classifications/' + data.id,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除资产分类
|
||||
export function delClassification(id) {
|
||||
export function delClassification(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/classification/' + id,
|
||||
method: 'delete'
|
||||
url: '/asset/classifications/' + id,
|
||||
method: 'delete',
|
||||
params: lastModUserId ? { lastModUserId } : {}
|
||||
})
|
||||
}
|
||||
|
||||
// 禁用资产分类
|
||||
export function disableClassification(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/classification/disable/' + id,
|
||||
url: '/asset/classifications/' + id + '/status/disable',
|
||||
method: 'put',
|
||||
params: { lastModUserId }
|
||||
params: lastModUserId ? { lastModUserId } : {}
|
||||
})
|
||||
}
|
||||
|
||||
// 启用资产分类
|
||||
export function enableClassification(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/classification/enable/' + id,
|
||||
url: '/asset/classifications/' + id + '/status/enable',
|
||||
method: 'put',
|
||||
params: { lastModUserId }
|
||||
params: lastModUserId ? { lastModUserId } : {}
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除资产分类
|
||||
export function delClassificationBatch(ids, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/classification/batch/' + ids,
|
||||
url: '/asset/classifications/batch',
|
||||
method: 'delete',
|
||||
params: { lastModUserId }
|
||||
data: ids,
|
||||
params: lastModUserId ? { lastModUserId } : {}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取资产分类树形结构
|
||||
export function getClassificationTree() {
|
||||
export function getClassificationTree(query) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
const params = {}
|
||||
if (query) {
|
||||
Object.keys(query).forEach(key => {
|
||||
if (key === 'classificationCode') {
|
||||
params.classification_code = query[key]
|
||||
} else if (key === 'classificationName') {
|
||||
params.classification_name = query[key]
|
||||
} else {
|
||||
params[key] = query[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return request({
|
||||
url: '/asset/classification/tree',
|
||||
method: 'get'
|
||||
url: '/asset/classifications/tree',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 根据父ID查询子分类
|
||||
export function getChildClassifications(parentId, query) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
const params = {}
|
||||
if (query) {
|
||||
Object.keys(query).forEach(key => {
|
||||
if (key === 'classificationCode') {
|
||||
params.classification_code = query[key]
|
||||
} else if (key === 'classificationName') {
|
||||
params.classification_name = query[key]
|
||||
} else {
|
||||
params[key] = query[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return request({
|
||||
url: '/asset/classifications/children/' + parentId,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 检查分类是否被使用
|
||||
export function checkClassificationInUse(id) {
|
||||
return request({
|
||||
url: '/asset/classification/' + id + '/check',
|
||||
url: '/asset/classifications/' + id + '/check',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出资产分类
|
||||
export function exportClassification(query) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
const params = {}
|
||||
if (query) {
|
||||
Object.keys(query).forEach(key => {
|
||||
if (key === 'classificationCode') {
|
||||
params.classification_code = query[key]
|
||||
} else if (key === 'classificationName') {
|
||||
params.classification_name = query[key]
|
||||
} else {
|
||||
params[key] = query[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return request({
|
||||
url: '/asset/classification/export',
|
||||
url: '/asset/classifications/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
params: params
|
||||
})
|
||||
}
|
150
pc/src/api/asset/inventory.js
Normal file
150
pc/src/api/asset/inventory.js
Normal file
@ -0,0 +1,150 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询资产清单列表
|
||||
export function listAsset(query) {
|
||||
return request({
|
||||
url: '/asset/inventory/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询资产详细
|
||||
export function getAsset(id) {
|
||||
return request({
|
||||
url: '/asset/inventory/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增资产
|
||||
export function addAsset(data) {
|
||||
return request({
|
||||
url: '/asset/inventory',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改资产
|
||||
export function updateAsset(data) {
|
||||
return request({
|
||||
url: '/asset/inventory',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除资产
|
||||
export function delAsset(id) {
|
||||
return request({
|
||||
url: '/asset/inventory/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除资产
|
||||
export function delAssetBatch(ids) {
|
||||
return request({
|
||||
url: '/asset/inventory/batch',
|
||||
method: 'delete',
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
||||
// 导出资产清单
|
||||
export function exportAsset(query) {
|
||||
return request({
|
||||
url: '/asset/inventory/export',
|
||||
method: 'get',
|
||||
params: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 下载导入模板
|
||||
export function downloadTemplate() {
|
||||
return request({
|
||||
url: '/asset/inventory/template',
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 导入资产数据
|
||||
export function importAsset(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return request({
|
||||
url: '/asset/inventory/import',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取资产分类树形列表
|
||||
export function listAssetClassTree() {
|
||||
return request({
|
||||
url: '/asset/class/tree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取资产位置树形列表
|
||||
export function listAssetLocationTree() {
|
||||
return request({
|
||||
url: '/asset/location/tree',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取公司列表
|
||||
export function listCompanies() {
|
||||
return request({
|
||||
url: '/system/company/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户列表(用于选择管理员)
|
||||
export function listUsers(query) {
|
||||
return request({
|
||||
url: '/system/user/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 打印资产标签
|
||||
export function printAssetLabel(ids) {
|
||||
return request({
|
||||
url: '/asset/inventory/print',
|
||||
method: 'post',
|
||||
data: { ids }
|
||||
})
|
||||
}
|
||||
|
||||
// 获取资产状态列表
|
||||
export function getAssetStatusOptions() {
|
||||
return request({
|
||||
url: '/asset/inventory/status/options',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 上传资产照片
|
||||
export function uploadAssetImage(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return request({
|
||||
url: '/common/upload',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
77
pc/src/api/asset/label.js
Normal file
77
pc/src/api/asset/label.js
Normal file
@ -0,0 +1,77 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询标签模板列表
|
||||
export function listLabelTemplates() {
|
||||
return request({
|
||||
url: '/asset/label/template/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取标签模板详情
|
||||
export function getLabelTemplate(id) {
|
||||
return request({
|
||||
url: '/asset/label/template/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 添加标签模板
|
||||
export function addLabelTemplate(data) {
|
||||
return request({
|
||||
url: '/asset/label/template',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 更新标签模板
|
||||
export function updateLabelTemplate(data) {
|
||||
return request({
|
||||
url: '/asset/label/template',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除标签模板
|
||||
export function deleteLabelTemplate(id) {
|
||||
return request({
|
||||
url: '/asset/label/template/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 设置默认模板
|
||||
export function setDefaultTemplate(id) {
|
||||
return request({
|
||||
url: '/asset/label/template/default/' + id,
|
||||
method: 'put'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取可用的标签字段
|
||||
export function getLabelFields() {
|
||||
return request({
|
||||
url: '/asset/label/fields',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 打印标签预览
|
||||
export function previewLabel(data) {
|
||||
return request({
|
||||
url: '/asset/label/preview',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 打印标签
|
||||
export function printLabel(data) {
|
||||
return request({
|
||||
url: '/asset/label/print',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
@ -2,17 +2,31 @@ import request from '@/utils/request'
|
||||
|
||||
// 查询资产位置列表
|
||||
export function listLocation(query) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
const params = {}
|
||||
if (query) {
|
||||
Object.keys(query).forEach(key => {
|
||||
if (key === 'locationCode') {
|
||||
params.location_code = query[key]
|
||||
} else if (key === 'locationName') {
|
||||
params.location_name = query[key]
|
||||
} else {
|
||||
params[key] = query[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return request({
|
||||
url: '/asset/location/list',
|
||||
url: '/asset/locations/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 查询资产位置详情
|
||||
export function getLocation(id) {
|
||||
return request({
|
||||
url: '/asset/location/' + id,
|
||||
url: '/asset/locations/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
@ -20,7 +34,7 @@ export function getLocation(id) {
|
||||
// 新增资产位置
|
||||
export function addLocation(data) {
|
||||
return request({
|
||||
url: '/asset/location',
|
||||
url: '/asset/locations/create',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
@ -29,68 +43,134 @@ export function addLocation(data) {
|
||||
// 修改资产位置
|
||||
export function updateLocation(data) {
|
||||
return request({
|
||||
url: '/asset/location',
|
||||
url: '/asset/locations/' + data.id,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除资产位置
|
||||
export function delLocation(id) {
|
||||
export function delLocation(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/location/' + id,
|
||||
method: 'delete'
|
||||
url: '/asset/locations/' + id,
|
||||
method: 'delete',
|
||||
params: lastModUserId ? { lastModUserId } : {}
|
||||
})
|
||||
}
|
||||
|
||||
// 禁用资产位置
|
||||
export function disableLocation(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/location/disable/' + id,
|
||||
url: '/asset/locations/' + id + '/status/disable',
|
||||
method: 'put',
|
||||
params: { lastModUserId }
|
||||
params: lastModUserId ? { lastModUserId } : {}
|
||||
})
|
||||
}
|
||||
|
||||
// 启用资产位置
|
||||
export function enableLocation(id, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/location/enable/' + id,
|
||||
url: '/asset/locations/' + id + '/status/enable',
|
||||
method: 'put',
|
||||
params: { lastModUserId }
|
||||
params: lastModUserId ? { lastModUserId } : {}
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除资产位置
|
||||
export function delLocationBatch(ids, lastModUserId) {
|
||||
return request({
|
||||
url: '/asset/location/batch/' + ids,
|
||||
url: '/asset/locations/batch',
|
||||
method: 'delete',
|
||||
params: { lastModUserId }
|
||||
data: ids,
|
||||
params: lastModUserId ? { lastModUserId } : {}
|
||||
})
|
||||
}
|
||||
|
||||
// 获取资产位置树形结构
|
||||
export function getLocationTree() {
|
||||
export function getLocationTree(query) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
const params = {}
|
||||
if (query) {
|
||||
Object.keys(query).forEach(key => {
|
||||
if (key === 'locationCode') {
|
||||
params.location_code = query[key]
|
||||
} else if (key === 'locationName') {
|
||||
params.location_name = query[key]
|
||||
} else {
|
||||
params[key] = query[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return request({
|
||||
url: '/asset/location/tree',
|
||||
method: 'get'
|
||||
url: '/asset/locations/tree',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 根据父ID查询子位置
|
||||
export function getChildLocations(parentId, query) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
const params = {}
|
||||
if (query) {
|
||||
Object.keys(query).forEach(key => {
|
||||
if (key === 'locationCode') {
|
||||
params.location_code = query[key]
|
||||
} else if (key === 'locationName') {
|
||||
params.location_name = query[key]
|
||||
} else {
|
||||
params[key] = query[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return request({
|
||||
url: '/asset/locations/children/' + parentId,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 检查位置是否被使用
|
||||
export function checkLocationInUse(id) {
|
||||
return request({
|
||||
url: '/asset/location/' + id + '/check',
|
||||
url: '/asset/locations/' + id + '/check',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 检查编码唯一性
|
||||
export function checkLocationCode(id, locationCode) {
|
||||
return request({
|
||||
url: '/asset/locations/check/code',
|
||||
method: 'get',
|
||||
params: {
|
||||
id: id,
|
||||
location_code: locationCode
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 导出资产位置
|
||||
export function exportLocation(query) {
|
||||
// 将驼峰命名转换为下划线命名
|
||||
const params = {}
|
||||
if (query) {
|
||||
Object.keys(query).forEach(key => {
|
||||
if (key === 'locationCode') {
|
||||
params.location_code = query[key]
|
||||
} else if (key === 'locationName') {
|
||||
params.location_name = query[key]
|
||||
} else {
|
||||
params[key] = query[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return request({
|
||||
url: '/asset/location/export',
|
||||
url: '/asset/locations/export',
|
||||
method: 'get',
|
||||
params: query
|
||||
params: params
|
||||
})
|
||||
}
|
199
pc/src/api/room.js
Normal file
199
pc/src/api/room.js
Normal file
@ -0,0 +1,199 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询房源列表
|
||||
export function listRoom(query) {
|
||||
return request({
|
||||
url: '/room/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询房源详细信息
|
||||
export function getRoom(id) {
|
||||
return request({
|
||||
url: '/room/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增房源
|
||||
export function addRoom(data) {
|
||||
return request({
|
||||
url: '/room',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改房源
|
||||
export function updateRoom(data) {
|
||||
return request({
|
||||
url: '/room',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除房源
|
||||
export function delRoom(id) {
|
||||
return request({
|
||||
url: '/room/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除房源
|
||||
export function delRoomBatch(ids) {
|
||||
return request({
|
||||
url: '/room/batch/' + ids,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 导出房源
|
||||
export function exportRoom(query) {
|
||||
return request({
|
||||
url: '/room/template/export/batch',
|
||||
method: 'post',
|
||||
data: query,
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 下载导入模板
|
||||
export function downloadTemplate() {
|
||||
return request({
|
||||
url: '/room/template/download',
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
|
||||
// 导入房源数据
|
||||
export function importRoom(file) {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
return request({
|
||||
url: '/room/import',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 查询房源标签列表
|
||||
export function listRoomTags() {
|
||||
return request({
|
||||
url: '/room/tags',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取项目列表
|
||||
export function listProjects() {
|
||||
return request({
|
||||
url: '/project/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取楼宇列表
|
||||
export function listBuildings(projectId) {
|
||||
return request({
|
||||
url: '/building/list',
|
||||
method: 'get',
|
||||
params: { projectId }
|
||||
})
|
||||
}
|
||||
|
||||
// 获取楼层列表
|
||||
export function listFloors(buildingId) {
|
||||
return request({
|
||||
url: '/building/floors',
|
||||
method: 'get',
|
||||
params: { buildingId }
|
||||
})
|
||||
}
|
||||
|
||||
// 远程调用 - 获取业户列表
|
||||
export function listOwners(query) {
|
||||
return request({
|
||||
url: '/customer/owners',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 远程调用 - 检查房源合同状态
|
||||
export function checkRoomContractStatus(roomId) {
|
||||
return request({
|
||||
url: '/contract/check/room/' + roomId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 修改房源租赁状态
|
||||
export function updateRoomLeaseStatus(id, status) {
|
||||
return request({
|
||||
url: '/room/lease-status',
|
||||
method: 'put',
|
||||
data: { id, status }
|
||||
})
|
||||
}
|
||||
|
||||
// 检查房号是否存在
|
||||
export function checkRoomNumberExists(params) {
|
||||
return request({
|
||||
url: '/room/check-number',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 获取房源图片列表
|
||||
export function listRoomImages(params) {
|
||||
return request({
|
||||
url: '/room/images',
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
// 上传房源图片
|
||||
export function uploadRoomImage(roomId, imageType, file, sortOrder) {
|
||||
const formData = new FormData()
|
||||
formData.append('roomId', roomId)
|
||||
formData.append('imageType', imageType)
|
||||
formData.append('file', file)
|
||||
if (sortOrder !== undefined) {
|
||||
formData.append('sortOrder', sortOrder)
|
||||
}
|
||||
return request({
|
||||
url: '/room/upload',
|
||||
method: 'post',
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除房源图片
|
||||
export function deleteRoomImage(id) {
|
||||
return request({
|
||||
url: '/room/image/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 批量删除房源图片
|
||||
export function batchDeleteRoomImages(ids) {
|
||||
return request({
|
||||
url: '/room/images/batch',
|
||||
method: 'delete',
|
||||
data: { ids }
|
||||
})
|
||||
}
|
BIN
pc/src/assets/images/code.png
Normal file
BIN
pc/src/assets/images/code.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 270 KiB |
65
pc/src/assets/images/qrcode.svg
Normal file
65
pc/src/assets/images/qrcode.svg
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="200" height="200" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="200" height="200" fill="white"/>
|
||||
<!-- QR Code pattern -->
|
||||
<!-- Position detection patterns (corners) -->
|
||||
<!-- Top-left -->
|
||||
<rect x="20" y="20" width="40" height="40" fill="black"/>
|
||||
<rect x="28" y="28" width="24" height="24" fill="white"/>
|
||||
<rect x="36" y="36" width="8" height="8" fill="black"/>
|
||||
|
||||
<!-- Top-right -->
|
||||
<rect x="140" y="20" width="40" height="40" fill="black"/>
|
||||
<rect x="148" y="28" width="24" height="24" fill="white"/>
|
||||
<rect x="156" y="36" width="8" height="8" fill="black"/>
|
||||
|
||||
<!-- Bottom-left -->
|
||||
<rect x="20" y="140" width="40" height="40" fill="black"/>
|
||||
<rect x="28" y="148" width="24" height="24" fill="white"/>
|
||||
<rect x="36" y="156" width="8" height="8" fill="black"/>
|
||||
|
||||
<!-- Timing patterns -->
|
||||
<rect x="36" y="76" width="8" height="8" fill="black"/>
|
||||
<rect x="52" y="76" width="8" height="8" fill="black"/>
|
||||
<rect x="68" y="76" width="8" height="8" fill="black"/>
|
||||
<rect x="84" y="76" width="8" height="8" fill="black"/>
|
||||
<rect x="100" y="76" width="8" height="8" fill="black"/>
|
||||
<rect x="116" y="76" width="8" height="8" fill="black"/>
|
||||
<rect x="132" y="76" width="8" height="8" fill="black"/>
|
||||
<rect x="148" y="76" width="8" height="8" fill="black"/>
|
||||
|
||||
<rect x="76" y="36" width="8" height="8" fill="black"/>
|
||||
<rect x="76" y="52" width="8" height="8" fill="black"/>
|
||||
<rect x="76" y="68" width="8" height="8" fill="black"/>
|
||||
<rect x="76" y="84" width="8" height="8" fill="black"/>
|
||||
<rect x="76" y="100" width="8" height="8" fill="black"/>
|
||||
<rect x="76" y="116" width="8" height="8" fill="black"/>
|
||||
<rect x="76" y="132" width="8" height="8" fill="black"/>
|
||||
<rect x="76" y="148" width="8" height="8" fill="black"/>
|
||||
|
||||
<!-- Random QR code data blocks -->
|
||||
<rect x="52" y="36" width="8" height="8" fill="black"/>
|
||||
<rect x="68" y="52" width="8" height="8" fill="black"/>
|
||||
<rect x="84" y="84" width="8" height="8" fill="black"/>
|
||||
<rect x="100" y="116" width="8" height="8" fill="black"/>
|
||||
<rect x="116" y="132" width="8" height="8" fill="black"/>
|
||||
<rect x="132" y="148" width="8" height="8" fill="black"/>
|
||||
<rect x="148" y="100" width="8" height="8" fill="black"/>
|
||||
<rect x="36" y="116" width="8" height="8" fill="black"/>
|
||||
<rect x="52" y="132" width="8" height="8" fill="black"/>
|
||||
<rect x="116" y="36" width="8" height="8" fill="black"/>
|
||||
<rect x="132" y="52" width="8" height="8" fill="black"/>
|
||||
<rect x="92" y="140" width="8" height="8" fill="black"/>
|
||||
<rect x="108" y="92" width="8" height="8" fill="black"/>
|
||||
<rect x="124" y="108" width="8" height="8" fill="black"/>
|
||||
<rect x="44" y="100" width="8" height="8" fill="black"/>
|
||||
<rect x="60" y="92" width="8" height="8" fill="black"/>
|
||||
<rect x="100" y="44" width="8" height="8" fill="black"/>
|
||||
<rect x="92" y="60" width="8" height="8" fill="black"/>
|
||||
<rect x="156" y="92" width="8" height="8" fill="black"/>
|
||||
<rect x="140" y="108" width="8" height="8" fill="black"/>
|
||||
<rect x="124" y="156" width="8" height="8" fill="black"/>
|
||||
<rect x="108" y="140" width="8" height="8" fill="black"/>
|
||||
<rect x="44" y="156" width="8" height="8" fill="black"/>
|
||||
<rect x="60" y="140" width="8" height="8" fill="black"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
@ -9,6 +9,17 @@ import './assets/styles/index.scss'
|
||||
Vue.use(ElementUI)
|
||||
Vue.config.productionTip = false
|
||||
|
||||
// 添加下载方法
|
||||
Vue.prototype.download = function(res, fileName) {
|
||||
// 创建blob链接
|
||||
const blob = new Blob([res.data])
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = fileName
|
||||
link.click()
|
||||
URL.revokeObjectURL(link.href)
|
||||
}
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
|
@ -45,6 +45,18 @@ const routes = [
|
||||
component: () => import('@/views/asset/classification/index'),
|
||||
name: 'AssetClassification',
|
||||
meta: { title: '资产分类设置', icon: 'el-icon-folder' }
|
||||
},
|
||||
{
|
||||
path: 'inventory',
|
||||
component: () => import('@/views/asset/inventory/index'),
|
||||
name: 'AssetInventory',
|
||||
meta: { title: '资产清单管理', icon: 'el-icon-files' }
|
||||
},
|
||||
{
|
||||
path: 'labelPage',
|
||||
component: () => import('@/views/asset/labelPage/index'),
|
||||
name: 'AssetLabel',
|
||||
meta: { title: '资产标签设置', icon: 'el-icon-printer' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -16,6 +16,12 @@ export default {
|
||||
name: 'BuildingList',
|
||||
component: () => import('@/views/project/building/index'),
|
||||
meta: { title: '楼宇列表' }
|
||||
},
|
||||
{
|
||||
path: 'room',
|
||||
name: 'RoomList',
|
||||
component: () => import('@/views/project/room/index'),
|
||||
meta: { title: '房源列表' }
|
||||
}
|
||||
]
|
||||
}
|
@ -4,7 +4,7 @@ import { Message } from 'element-ui'
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
// baseURL: '/api', // 修改为相对路径,使用代理
|
||||
baseURL: 'http://192.168.137.38:8080/api', // 接口地址
|
||||
baseURL: 'http://192.168.137.3:8080/api', // 接口地址
|
||||
|
||||
timeout: 10000 // 请求超时时间
|
||||
})
|
||||
@ -24,15 +24,20 @@ service.interceptors.request.use(
|
||||
// 响应拦截器
|
||||
service.interceptors.response.use(
|
||||
response => {
|
||||
// 如果是blob类型(文件下载),直接返回
|
||||
if (response.config.responseType === 'blob') {
|
||||
return response
|
||||
}
|
||||
|
||||
const res = response.data
|
||||
// 如果返回的状态码不是200,则判断为错误
|
||||
// 如果返回的状态码不是000000,则判断为错误
|
||||
if (res.code !== '000000') {
|
||||
Message({
|
||||
message: res.message || '系统错误',
|
||||
message: res.msg || '系统错误',
|
||||
type: 'error',
|
||||
duration: 5 * 1000
|
||||
})
|
||||
return Promise.reject(new Error(res.message || '系统错误'))
|
||||
return Promise.reject(new Error(res.msg || '系统错误'))
|
||||
} else {
|
||||
return res
|
||||
}
|
||||
|
@ -4,17 +4,24 @@
|
||||
<!-- 左侧树形结构 -->
|
||||
<el-col :span="6">
|
||||
<div class="tree-container">
|
||||
<div class="tree-header">
|
||||
<span class="tree-title">资产分类</span>
|
||||
</div>
|
||||
<el-divider class="tree-divider"></el-divider>
|
||||
<el-tree
|
||||
ref="classTree"
|
||||
:data="classTree"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
highlight-current
|
||||
@node-click="handleNodeClick"
|
||||
:default-expanded-keys="defaultExpandedKeys">
|
||||
<span class="custom-tree-node" slot-scope="{ node, data }">
|
||||
<span>{{ node.label }}</span>
|
||||
<span v-if="data.children && data.children.length > 0" class="node-count">
|
||||
({{ data.children.length }})
|
||||
</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
@ -26,11 +33,18 @@
|
||||
<div slot="header" class="clearfix">
|
||||
<span>分类列表</span>
|
||||
<el-button
|
||||
style="float: right;"
|
||||
style="float: right; margin-left: 10px;"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd">新增分类</el-button>
|
||||
<el-button
|
||||
style="float: right;"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="multipleSelection.length === 0"
|
||||
@click="handleBatchDelete">批量删除</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
@ -53,10 +67,21 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="classList" border stripe style="width: 100%">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="classList"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="classificationCode" label="分类编码" width="150" />
|
||||
<el-table-column prop="classificationName" label="分类名称" width="200" />
|
||||
<el-table-column prop="parentName" label="上级分类" width="200" />
|
||||
<el-table-column label="上级分类" width="200">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ getParentName(scope.row.parentId) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === '1' ? 'success' : 'info'">
|
||||
@ -108,19 +133,21 @@
|
||||
<el-input v-model="form.classificationName" placeholder="请输入分类名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上级分类" prop="parentId">
|
||||
<el-tree-select
|
||||
<el-cascader
|
||||
v-model="form.parentId"
|
||||
:data="classTree"
|
||||
:props="defaultProps"
|
||||
placeholder="请选择上级分类"
|
||||
:options="classTreeOptions"
|
||||
:props="{
|
||||
checkStrictly: true,
|
||||
emitPath: false,
|
||||
expandTrigger: 'hover',
|
||||
value: 'id',
|
||||
label: 'label'
|
||||
}"
|
||||
clearable
|
||||
filterable
|
||||
check-strictly
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:render-after-expand="false"
|
||||
placeholder="请选择上级分类"
|
||||
style="width: 100%">
|
||||
</el-tree-select>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
@ -141,7 +168,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listClassification, getClassification, addClassification, updateClassification, delClassification, enableClassification, disableClassification, getClassificationTree, checkClassificationInUse } from '@/api/asset/classification'
|
||||
import { listClassification, getClassification, addClassification, updateClassification, delClassification, enableClassification, disableClassification, getClassificationTree, checkClassificationInUse, getChildClassifications, delClassificationBatch } from '@/api/asset/classification'
|
||||
|
||||
export default {
|
||||
name: 'AssetClassification',
|
||||
@ -155,12 +182,14 @@ export default {
|
||||
classList: [],
|
||||
// 分类树形数据
|
||||
classTree: [],
|
||||
// 处理后的树形选项数据(用于级联选择器)
|
||||
classTreeOptions: [],
|
||||
// 默认展开的节点
|
||||
defaultExpandedKeys: [],
|
||||
// 树形配置
|
||||
// 树形配置(用于树组件)
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'classificationName'
|
||||
label: 'label'
|
||||
},
|
||||
// 弹出层标题
|
||||
dialogTitle: '',
|
||||
@ -193,7 +222,9 @@ export default {
|
||||
remark: [
|
||||
{ max: 200, message: '长度不能超过200个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
},
|
||||
// 多选选中项
|
||||
multipleSelection: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -201,33 +232,206 @@ export default {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 获取分类树形结构 */
|
||||
getTree() {
|
||||
getClassificationTree().then(res => {
|
||||
if (res.code === 200) {
|
||||
this.classTree = res.data
|
||||
// 默认展开第一级节点
|
||||
if (this.classTree.length > 0) {
|
||||
this.defaultExpandedKeys = [this.classTree[0].id]
|
||||
}
|
||||
/** 展开所有节点 */
|
||||
expandAll() {
|
||||
const allKeys = this.getExpandedKeys(this.classTree)
|
||||
this.defaultExpandedKeys = allKeys
|
||||
this.$refs.classTree.setCheckedKeys([]) // 清空选中状态
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.classTree) {
|
||||
this.$refs.classTree.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 收起所有节点 */
|
||||
collapseAll() {
|
||||
this.defaultExpandedKeys = []
|
||||
this.$refs.classTree.setCheckedKeys([]) // 清空选中状态
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.classTree) {
|
||||
this.$refs.classTree.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取分类树形结构 */
|
||||
getTree() {
|
||||
this.loading = true
|
||||
const params = {
|
||||
status: undefined // 获取所有状态的分类,包括启用和禁用
|
||||
}
|
||||
getClassificationTree(params).then(res => {
|
||||
if (res.code === '000000') {
|
||||
// 处理返回的树形数据,确保字段名称正确
|
||||
this.classTree = this.processTreeData(res.data)
|
||||
|
||||
// 准备级联选择器的数据源
|
||||
this.classTreeOptions = this.prepareTreeSelectOptions(this.classTree)
|
||||
|
||||
// 如果之前已经有展开的节点,保持这些节点展开
|
||||
// 否则只展开第一级有子节点的节点
|
||||
if (!this.defaultExpandedKeys || this.defaultExpandedKeys.length === 0) {
|
||||
this.defaultExpandedKeys = this.getFirstLevelKeys(this.classTree)
|
||||
}
|
||||
|
||||
// 强制刷新树组件
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.classTree) {
|
||||
this.$refs.classTree.$forceUpdate()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取分类树失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error('获取分类树失败', error)
|
||||
this.$message.error('系统错误,获取分类树失败')
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取第一级节点的key,仅展开有子节点的 */
|
||||
getFirstLevelKeys(tree) {
|
||||
if (!tree || !Array.isArray(tree)) return []
|
||||
|
||||
return tree
|
||||
.filter(node => node.children && node.children.length > 0)
|
||||
.map(node => node.id)
|
||||
},
|
||||
|
||||
/** 获取所有节点key,用于全部展开 */
|
||||
getExpandedKeys(tree, keys = []) {
|
||||
for (const node of tree) {
|
||||
if (node.children && node.children.length > 0) {
|
||||
keys.push(node.id)
|
||||
this.getExpandedKeys(node.children, keys)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
},
|
||||
|
||||
/** 处理树形数据,确保字段名称正确 */
|
||||
processTreeData(data) {
|
||||
if (!data || !Array.isArray(data)) return []
|
||||
|
||||
const processNode = (node) => {
|
||||
// 创建一个新对象,使用驼峰命名
|
||||
const processedNode = {
|
||||
id: node.id,
|
||||
// 转换label,格式为"编码-名称"
|
||||
label: `${node.code || ''}-${node.classificationName || node.label || ''}`,
|
||||
// 驼峰命名
|
||||
classificationName: node.classificationName || node.label || '',
|
||||
classificationCode: node.code || '',
|
||||
status: node.status || '1',
|
||||
parentId: node.parentId || null,
|
||||
children: []
|
||||
}
|
||||
|
||||
// 递归处理子节点
|
||||
if (node.children && Array.isArray(node.children) && node.children.length > 0) {
|
||||
processedNode.children = node.children.map(child => processNode(child))
|
||||
}
|
||||
|
||||
return processedNode
|
||||
}
|
||||
|
||||
return data.map(item => processNode(item))
|
||||
},
|
||||
|
||||
/** 准备级联选择器的数据源 */
|
||||
prepareTreeSelectOptions(data) {
|
||||
if (!data || !Array.isArray(data)) return []
|
||||
|
||||
// 创建一个无上级选项
|
||||
const options = [{
|
||||
id: '',
|
||||
label: '无上级分类',
|
||||
children: []
|
||||
}]
|
||||
|
||||
// 添加所有分类节点
|
||||
const cloneData = JSON.parse(JSON.stringify(data))
|
||||
options.push(...cloneData)
|
||||
|
||||
return options
|
||||
},
|
||||
|
||||
/** 查询分类列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listClassification(this.queryParams).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.classList = res.data.rows
|
||||
if (res.code === '000000') {
|
||||
// 转换API返回数据结构为驼峰命名
|
||||
this.classList = this.convertSnakeToCamel(res.data.list)
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取分类列表失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error('获取分类列表失败', error)
|
||||
this.$message.error('系统错误,获取分类列表失败')
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 转换下划线命名为驼峰命名 */
|
||||
convertSnakeToCamel(list) {
|
||||
if (!list || !Array.isArray(list)) return []
|
||||
|
||||
return list.map(item => {
|
||||
return {
|
||||
id: item.id,
|
||||
classificationCode: item.classification_code || item.classificationCode || '',
|
||||
classificationName: item.classification_name || item.classificationName || '',
|
||||
parentId: item.parent_id || item.parentId || null,
|
||||
status: item.status || '1',
|
||||
remark: item.remark || '',
|
||||
createTime: item.create_time || item.createTime || '',
|
||||
createUserId: item.create_user_id || item.createUserId || '',
|
||||
lastModUserId: item.last_mod_user_id || item.lastModUserId || '',
|
||||
lastModTime: item.last_mod_time || item.lastModTime || ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 将驼峰命名转换为下划线命名(API请求前的转换) */
|
||||
convertCamelToSnake(data) {
|
||||
const result = {}
|
||||
|
||||
Object.keys(data).forEach(key => {
|
||||
// 将驼峰转换为下划线
|
||||
const newKey = key.replace(/([A-Z])/g, '_$1').toLowerCase()
|
||||
result[newKey] = data[key]
|
||||
})
|
||||
|
||||
return result
|
||||
},
|
||||
|
||||
/** 节点单击事件 */
|
||||
handleNodeClick(data) {
|
||||
this.queryParams.parentId = data.id
|
||||
this.getList()
|
||||
// 使用新的获取子分类API
|
||||
this.loading = true
|
||||
getChildClassifications(data.id, {
|
||||
pageNum: this.queryParams.pageNum,
|
||||
pageSize: this.queryParams.pageSize
|
||||
}).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.classList = this.convertSnakeToCamel(res.data.list)
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取子分类列表失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error('获取子分类列表失败', error)
|
||||
this.$message.error('系统错误,获取子分类列表失败')
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
@ -268,12 +472,19 @@ export default {
|
||||
handleEdit(row) {
|
||||
const id = row.id
|
||||
getClassification(id).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.form = res.data
|
||||
// 确保parentId是字符串或数字类型,而不是对象
|
||||
if (this.form.parentId && typeof this.form.parentId === 'object') {
|
||||
this.form.parentId = this.form.parentId.id || ''
|
||||
if (res.code === '000000') {
|
||||
// 将API响应数据映射到表单字段,使用驼峰命名
|
||||
this.form = {
|
||||
id: res.data.id,
|
||||
classificationCode: res.data.classificationCode,
|
||||
classificationName: res.data.classificationName,
|
||||
parentId: res.data.parentId || '',
|
||||
status: res.data.status,
|
||||
remark: res.data.remark,
|
||||
// 添加最后修改人
|
||||
lastModUserId: this.$store.getters.userId || ''
|
||||
}
|
||||
|
||||
this.dialogTitle = '编辑分类'
|
||||
this.dialogVisible = true
|
||||
// 重置表单验证
|
||||
@ -282,21 +493,56 @@ export default {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取分类详情失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取分类详情失败', error)
|
||||
this.$message.error('系统错误,获取分类详情失败')
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
// A. 构建提交数据,映射表单字段到API字段
|
||||
const submitData = {
|
||||
id: this.form.id,
|
||||
classificationCode: this.form.classificationCode,
|
||||
classificationName: this.form.classificationName,
|
||||
parentId: this.form.parentId,
|
||||
status: this.form.status,
|
||||
remark: this.form.remark
|
||||
}
|
||||
|
||||
// B. 添加创建/修改用户信息
|
||||
if (this.form.id) {
|
||||
submitData.lastModUserId = this.form.lastModUserId || this.$store.getters.userId || ''
|
||||
} else {
|
||||
submitData.createUserId = this.$store.getters.userId || ''
|
||||
}
|
||||
|
||||
const method = this.form.id ? updateClassification : addClassification
|
||||
method(this.form).then(res => {
|
||||
if (res.code === 200) {
|
||||
method(submitData).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.$message.success('操作成功')
|
||||
this.dialogVisible = false
|
||||
|
||||
// 重新获取树形结构,确保更新多级分类
|
||||
this.getTree()
|
||||
|
||||
// 如果是新增子分类,更新当前列表
|
||||
if (this.queryParams.parentId && !this.form.id) {
|
||||
this.handleNodeClick({ id: this.queryParams.parentId })
|
||||
} else {
|
||||
this.getList()
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.msg || '操作失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('提交表单失败', error)
|
||||
this.$message.error('系统错误,提交失败')
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -310,17 +556,24 @@ export default {
|
||||
}).then(() => {
|
||||
// 先检查分类是否被使用
|
||||
checkClassificationInUse(row.id).then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
if (res.code === '000000' && res.data) {
|
||||
this.$message.error('该分类已被使用,无法删除')
|
||||
return
|
||||
}
|
||||
// 未被使用,执行删除操作
|
||||
delClassification(row.id).then(res => {
|
||||
if (res.code === 200) {
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
delClassification(row.id, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.$message.success('删除成功')
|
||||
// 重新获取树形结构,确保更新多级分类
|
||||
this.getTree()
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.msg || '删除失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('删除操作失败', error)
|
||||
this.$message.error('系统错误,删除失败')
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
@ -334,13 +587,20 @@ export default {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 获取当前用户ID
|
||||
const lastModUserId = this.$store.getters.userId || '1'
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
const method = row.status === '1' ? disableClassification : enableClassification
|
||||
method(row.id, lastModUserId).then(res => {
|
||||
if (res.code === 200) {
|
||||
if (res.code === '000000') {
|
||||
this.$message.success(`${action}成功`)
|
||||
// 状态变更后,可能会影响树的显示,所以也需要刷新树
|
||||
this.getTree()
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.msg || `${action}失败`)
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(`${action}操作失败`, error)
|
||||
this.$message.error(`系统错误,${action}失败`)
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
@ -353,6 +613,71 @@ export default {
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
},
|
||||
/** 获取上级分类名称 */
|
||||
getParentName(parentId) {
|
||||
if (!parentId) return '无';
|
||||
|
||||
// 先在树形数据中查找
|
||||
const findParentInTree = (tree, id) => {
|
||||
for (const node of tree) {
|
||||
if (node.id === id) {
|
||||
return node.classificationCode + '-' + node.classificationName;
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
const found = findParentInTree(node.children, id);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const parentName = findParentInTree(this.classTree, parentId);
|
||||
if (parentName) return parentName;
|
||||
|
||||
// 如果在树中没找到,可能在列表数据中
|
||||
const parent = this.classList.find(item => item.id === parentId);
|
||||
return parent ? (parent.classificationCode + '-' + parent.classificationName) : '未知';
|
||||
},
|
||||
/** 多选选中项改变 */
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
/** 批量删除按钮操作 */
|
||||
handleBatchDelete() {
|
||||
if (this.multipleSelection.length === 0) {
|
||||
this.$message.warning('请至少选择一个分类')
|
||||
return
|
||||
}
|
||||
|
||||
this.$confirm('是否确认删除选中的分类?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 获取选中的分类ID列表
|
||||
const selectedIds = this.multipleSelection.map(item => item.id)
|
||||
// 获取当前用户ID
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
|
||||
// 执行批量删除操作
|
||||
this.loading = true
|
||||
delClassificationBatch(selectedIds, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.$message.success('批量删除成功')
|
||||
// 重新获取树形结构,确保更新多级分类
|
||||
this.getTree()
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.msg || '批量删除失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error('批量删除操作失败', error)
|
||||
this.$message.error('系统错误,批量删除失败')
|
||||
this.loading = false
|
||||
})
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -370,6 +695,28 @@ export default {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tree-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tree-title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.tree-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tree-divider {
|
||||
margin: 5px 0 15px;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@ -383,6 +730,12 @@ export default {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.node-count {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
text-align: right;
|
||||
@ -412,5 +765,14 @@ export default {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 树节点样式优化 */
|
||||
::v-deep .el-tree-node__content {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
::v-deep .el-tree-node__expand-icon {
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
152
pc/src/views/asset/inventory/components/AssetDetail.vue
Normal file
152
pc/src/views/asset/inventory/components/AssetDetail.vue
Normal file
@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<div class="asset-detail-container" v-loading="loading">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基础信息" name="basic">
|
||||
<el-descriptions class="margin-top" title="资产基础信息" :column="3" border>
|
||||
<el-descriptions-item label="资产编码">{{ assetDetail.code }}</el-descriptions-item>
|
||||
<el-descriptions-item label="资产分类">{{ assetDetail.classificationName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="资产名称">{{ assetDetail.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="资产状态">
|
||||
<el-tag :type="getStatusType(assetDetail.status)">{{ getStatusText(assetDetail.status) }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="品牌">{{ assetDetail.brand }}</el-descriptions-item>
|
||||
<el-descriptions-item label="型号">{{ assetDetail.model }}</el-descriptions-item>
|
||||
<el-descriptions-item label="设备序列号">{{ assetDetail.serialNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="管理员">{{ assetDetail.administratorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属公司">{{ assetDetail.companyName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所在位置">{{ assetDetail.locationName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="购置方式">
|
||||
{{ assetDetail.purchaseType === '1' ? '采购' : assetDetail.purchaseType === '2' ? '租赁' : '-' }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="购置金额(含税)">{{ assetDetail.purchaseAmount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="购置时间">{{ assetDetail.purchaseTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="入库时间">{{ assetDetail.storageTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预计使用期限(月)">{{ assetDetail.expectedUsePeriod }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预计折旧期限(月)">{{ assetDetail.expectedDepreciationPeriod }}</el-descriptions-item>
|
||||
<el-descriptions-item label="保养到期时间">{{ assetDetail.maintenanceTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="保养说明" :span="3">{{ assetDetail.maintenanceDescription }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注" :span="3">{{ assetDetail.remark }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="image-section" v-if="assetDetail.imageUrl">
|
||||
<div class="section-title">资产照片</div>
|
||||
<div class="image-container">
|
||||
<el-image
|
||||
:src="assetDetail.imageUrl"
|
||||
:preview-src-list="[assetDetail.imageUrl]"
|
||||
fit="cover"
|
||||
style="width: 300px; height: 200px; border-radius: 4px;">
|
||||
</el-image>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAsset } from '@/api/asset/inventory'
|
||||
|
||||
export default {
|
||||
name: 'AssetDetail',
|
||||
props: {
|
||||
assetId: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
activeTab: 'basic',
|
||||
assetDetail: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
assetId: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.getAssetDetail()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 获取资产详情 */
|
||||
getAssetDetail() {
|
||||
if (!this.assetId) return
|
||||
|
||||
this.loading = true
|
||||
getAsset(this.assetId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.assetDetail = response.data || {}
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取资产详情失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取资产状态显示文本 */
|
||||
getStatusText(status) {
|
||||
const statusMap = {
|
||||
'0': '空闲',
|
||||
'1': '在用',
|
||||
'2': '借用',
|
||||
'3': '派发中',
|
||||
'4': '退库中',
|
||||
'5': '借出中',
|
||||
'6': '归还中',
|
||||
'7': '维修中'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
},
|
||||
|
||||
/** 获取资产状态显示类型 */
|
||||
getStatusType(status) {
|
||||
const typeMap = {
|
||||
'0': 'success',
|
||||
'1': 'primary',
|
||||
'2': 'info',
|
||||
'3': 'warning',
|
||||
'4': 'warning',
|
||||
'5': 'warning',
|
||||
'6': 'warning',
|
||||
'7': 'danger'
|
||||
}
|
||||
return typeMap[status] || 'info'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.asset-detail-container {
|
||||
padding: 10px;
|
||||
|
||||
.margin-top {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 20px 0 15px;
|
||||
padding-left: 10px;
|
||||
border-left: 3px solid #409EFF;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
471
pc/src/views/asset/inventory/components/AssetForm.vue
Normal file
471
pc/src/views/asset/inventory/components/AssetForm.vue
Normal file
@ -0,0 +1,471 @@
|
||||
<template>
|
||||
<div class="asset-form-container" v-loading="loading">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="130px" size="small">
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- 基础信息 Tab -->
|
||||
<el-tab-pane label="基础信息" name="basic">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="资产分类" prop="classificationId">
|
||||
<el-cascader
|
||||
v-model="form.classificationId"
|
||||
:options="classificationOptions"
|
||||
:props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false }"
|
||||
placeholder="请选择资产分类"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="管理员" prop="administratorId">
|
||||
<el-select
|
||||
v-model="form.administratorId"
|
||||
placeholder="请选择管理员"
|
||||
filterable
|
||||
remote
|
||||
:remote-method="remoteSearchAdmin"
|
||||
:loading="adminLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in adminOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="所属公司" prop="companyId">
|
||||
<el-select v-model="form.companyId" placeholder="请选择所属公司" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in companyOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="所在位置" prop="locationId">
|
||||
<el-cascader
|
||||
v-model="form.locationId"
|
||||
:options="locationOptions"
|
||||
:props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false }"
|
||||
placeholder="请选择所在位置"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="购置方式" prop="purchaseType">
|
||||
<el-select v-model="form.purchaseType" placeholder="请选择购置方式" style="width: 100%">
|
||||
<el-option label="采购" value="1" />
|
||||
<el-option label="租赁" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="资产名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入资产名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="资产编码" prop="code">
|
||||
<el-input v-model="form.code" placeholder="请输入资产编码" :disabled="isEdit" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="品牌" prop="brand">
|
||||
<el-input v-model="form.brand" placeholder="请输入品牌" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="型号" prop="model">
|
||||
<el-input v-model="form.model" placeholder="请输入型号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="设备序列号" prop="serialNumber">
|
||||
<el-input v-model="form.serialNumber" placeholder="请输入设备序列号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="购置金额" prop="purchaseAmount">
|
||||
<el-input-number v-model="form.purchaseAmount" :precision="2" :step="0.1" :min="0" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="预计使用期限(月)" prop="expectedUsePeriod">
|
||||
<el-input-number v-model="form.expectedUsePeriod" :min="0" :step="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="购置时间" prop="purchaseTime">
|
||||
<el-date-picker
|
||||
v-model="form.purchaseTime"
|
||||
type="date"
|
||||
placeholder="请选择购置时间"
|
||||
style="width: 100%"
|
||||
value-format="yyyy-MM-dd"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="入库时间" prop="storageTime">
|
||||
<el-date-picker
|
||||
v-model="form.storageTime"
|
||||
type="date"
|
||||
placeholder="请选择入库时间"
|
||||
style="width: 100%"
|
||||
value-format="yyyy-MM-dd"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="保养到期时间" prop="maintenanceTime">
|
||||
<el-date-picker
|
||||
v-model="form.maintenanceTime"
|
||||
type="date"
|
||||
placeholder="请选择保养到期时间"
|
||||
style="width: 100%"
|
||||
value-format="yyyy-MM-dd"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="预计折旧期限(月)" prop="expectedDepreciationPeriod">
|
||||
<el-input-number v-model="form.expectedDepreciationPeriod" :min="0" :step="1" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="保养说明" prop="maintenanceDescription">
|
||||
<el-input
|
||||
v-model="form.maintenanceDescription"
|
||||
type="textarea"
|
||||
placeholder="请输入保养说明"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
:rows="3"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="form.remark"
|
||||
type="textarea"
|
||||
placeholder="请输入备注"
|
||||
maxlength="200"
|
||||
show-word-limit
|
||||
:rows="3"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="资产照片" prop="imageUrl">
|
||||
<el-upload
|
||||
class="asset-uploader"
|
||||
:action="uploadImageUrl"
|
||||
:show-file-list="false"
|
||||
:on-success="handleImageSuccess"
|
||||
:on-error="handleImageError"
|
||||
:before-upload="beforeImageUpload"
|
||||
>
|
||||
<img v-if="form.imageUrl" :src="form.imageUrl" class="asset-image">
|
||||
<i v-else class="el-icon-plus asset-uploader-icon"></i>
|
||||
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过2MB</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="form-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAsset, addAsset, updateAsset, listAssetClassTree, listAssetLocationTree, listCompanies, listUsers, uploadAssetImage } from '@/api/asset/inventory'
|
||||
|
||||
export default {
|
||||
name: 'AssetForm',
|
||||
props: {
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
assetId: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 当前激活的选项卡
|
||||
activeTab: 'basic',
|
||||
// 管理员搜索加载中
|
||||
adminLoading: false,
|
||||
// 资产分类选项
|
||||
classificationOptions: [],
|
||||
// 资产位置选项
|
||||
locationOptions: [],
|
||||
// 公司选项
|
||||
companyOptions: [],
|
||||
// 管理员选项
|
||||
adminOptions: [],
|
||||
// 上传图片地址
|
||||
uploadImageUrl: process.env.VUE_APP_BASE_API + '/common/upload',
|
||||
// 表单对象
|
||||
form: {
|
||||
id: undefined,
|
||||
classificationId: undefined,
|
||||
administratorId: undefined,
|
||||
companyId: undefined,
|
||||
locationId: undefined,
|
||||
purchaseType: '1',
|
||||
name: '',
|
||||
code: '',
|
||||
brand: '',
|
||||
model: '',
|
||||
serialNumber: '',
|
||||
purchaseAmount: undefined,
|
||||
expectedUsePeriod: undefined,
|
||||
remark: '',
|
||||
purchaseTime: '',
|
||||
storageTime: '',
|
||||
imageUrl: '',
|
||||
maintenanceTime: '',
|
||||
maintenanceDescription: '',
|
||||
expectedDepreciationPeriod: undefined
|
||||
},
|
||||
// 表单校验规则
|
||||
rules: {
|
||||
classificationId: [
|
||||
{ required: true, message: '请选择资产分类', trigger: 'change' }
|
||||
],
|
||||
administratorId: [
|
||||
{ required: true, message: '请选择管理员', trigger: 'change' }
|
||||
],
|
||||
companyId: [
|
||||
{ required: true, message: '请选择所属公司', trigger: 'change' }
|
||||
],
|
||||
locationId: [
|
||||
{ required: true, message: '请选择所在位置', trigger: 'change' }
|
||||
],
|
||||
purchaseType: [
|
||||
{ required: true, message: '请选择购置方式', trigger: 'change' }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '请输入资产编码', trigger: 'blur' }
|
||||
],
|
||||
brand: [
|
||||
{ required: true, message: '请输入品牌', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getClassificationOptions()
|
||||
this.getLocationOptions()
|
||||
this.getCompanyOptions()
|
||||
this.remoteSearchAdmin('')
|
||||
|
||||
if (this.isEdit && this.assetId) {
|
||||
this.getAssetDetail()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 获取资产详情 */
|
||||
getAssetDetail() {
|
||||
this.loading = true
|
||||
getAsset(this.assetId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.form = response.data || {}
|
||||
|
||||
// 如果有管理员,添加到选项中
|
||||
if (this.form.administratorId && this.form.administratorName) {
|
||||
this.adminOptions = [
|
||||
{ id: this.form.administratorId, name: this.form.administratorName }
|
||||
]
|
||||
}
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取资产详情失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取资产分类树形选项 */
|
||||
getClassificationOptions() {
|
||||
listAssetClassTree().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.classificationOptions = response.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取资产位置树形选项 */
|
||||
getLocationOptions() {
|
||||
listAssetLocationTree().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.locationOptions = response.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取公司选项 */
|
||||
getCompanyOptions() {
|
||||
listCompanies().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.companyOptions = response.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 远程搜索管理员 */
|
||||
remoteSearchAdmin(query) {
|
||||
this.adminLoading = true
|
||||
listUsers({ name: query }).then(response => {
|
||||
this.adminLoading = false
|
||||
if (response.code === '000000') {
|
||||
this.adminOptions = response.data || []
|
||||
}
|
||||
}).catch(() => {
|
||||
this.adminLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 提交表单 */
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
|
||||
const formData = { ...this.form }
|
||||
|
||||
const method = this.isEdit ? updateAsset : addAsset
|
||||
method(formData).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.$message.success(this.isEdit ? '修改成功' : '新增成功')
|
||||
this.$emit('refresh')
|
||||
this.$emit('close')
|
||||
} else {
|
||||
this.$message.error(response.msg || (this.isEdit ? '修改失败' : '新增失败'))
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.$emit('close')
|
||||
},
|
||||
|
||||
/** 图片上传前校验 */
|
||||
beforeImageUpload(file) {
|
||||
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
|
||||
const isLt2M = file.size / 1024 / 1024 < 2
|
||||
|
||||
if (!isJPG) {
|
||||
this.$message.error('上传图片只能是 JPG/PNG 格式!')
|
||||
}
|
||||
if (!isLt2M) {
|
||||
this.$message.error('上传图片大小不能超过 2MB!')
|
||||
}
|
||||
return isJPG && isLt2M
|
||||
},
|
||||
|
||||
/** 图片上传成功回调 */
|
||||
handleImageSuccess(response) {
|
||||
if (response.code === '000000') {
|
||||
this.form.imageUrl = response.data.url
|
||||
} else {
|
||||
this.$message.error('上传图片失败')
|
||||
}
|
||||
},
|
||||
|
||||
/** 图片上传失败回调 */
|
||||
handleImageError() {
|
||||
this.$message.error('上传图片失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.asset-form-container {
|
||||
padding: 10px;
|
||||
|
||||
.form-footer {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.asset-uploader {
|
||||
::v-deep .el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.asset-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
line-height: 150px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.asset-image {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
::v-deep .el-input-number {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
358
pc/src/views/asset/inventory/components/AssetLabelPrint.vue
Normal file
358
pc/src/views/asset/inventory/components/AssetLabelPrint.vue
Normal file
@ -0,0 +1,358 @@
|
||||
<template>
|
||||
<div class="asset-print-container" v-loading="loading">
|
||||
<div class="print-options">
|
||||
<div class="option-item">
|
||||
<span class="option-label">标签模板:</span>
|
||||
<el-select v-model="printParams.templateId" placeholder="请选择标签模板" style="width: 200px">
|
||||
<el-option
|
||||
v-for="item in templateOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<span class="option-label">纸张类型:</span>
|
||||
<el-select v-model="printParams.paperType" placeholder="请选择纸张类型" style="width: 200px">
|
||||
<el-option label="A4" value="A4" />
|
||||
<el-option label="A5" value="A5" />
|
||||
<el-option label="标签纸" value="LABEL" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="option-item">
|
||||
<span class="option-label">显示内容:</span>
|
||||
<el-checkbox-group v-model="printParams.showFields">
|
||||
<el-checkbox label="code">资产编码</el-checkbox>
|
||||
<el-checkbox label="name">资产名称</el-checkbox>
|
||||
<el-checkbox label="qrCode">二维码</el-checkbox>
|
||||
<el-checkbox label="barCode">条形码</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-container">
|
||||
<div class="preview-title">打印预览</div>
|
||||
<div class="preview-content" ref="previewContent">
|
||||
<div v-for="(asset, index) in assetsToPrint" :key="index" class="label-item">
|
||||
<div class="label-header">资产标签</div>
|
||||
<div class="label-content">
|
||||
<div v-if="printParams.showFields.includes('code')" class="label-field">
|
||||
<span class="field-label">资产编码:</span>
|
||||
<span class="field-value">{{ asset.code }}</span>
|
||||
</div>
|
||||
<div v-if="printParams.showFields.includes('name')" class="label-field">
|
||||
<span class="field-label">资产名称:</span>
|
||||
<span class="field-value">{{ asset.name }}</span>
|
||||
</div>
|
||||
<!-- 模拟二维码 -->
|
||||
<div v-if="printParams.showFields.includes('qrCode')" class="label-qrcode">
|
||||
<div class="mock-qrcode"></div>
|
||||
</div>
|
||||
<!-- 模拟条形码 -->
|
||||
<div v-if="printParams.showFields.includes('barCode')" class="label-barcode">
|
||||
<div class="mock-barcode"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="print-actions">
|
||||
<el-button type="primary" icon="el-icon-printer" @click="handlePrint">打印</el-button>
|
||||
<el-button @click="cancel">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { printAssetLabel } from '@/api/asset/inventory'
|
||||
|
||||
export default {
|
||||
name: 'AssetLabelPrint',
|
||||
props: {
|
||||
ids: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
assetsToPrint: [],
|
||||
templateOptions: [
|
||||
{ id: '1', name: '默认标签模板' },
|
||||
{ id: '2', name: '简洁标签模板' },
|
||||
{ id: '3', name: '详细标签模板' }
|
||||
],
|
||||
printParams: {
|
||||
templateId: '1',
|
||||
paperType: 'A4',
|
||||
showFields: ['code', 'name', 'qrCode']
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getPrintData()
|
||||
},
|
||||
methods: {
|
||||
/** 获取打印数据 */
|
||||
getPrintData() {
|
||||
if (!this.ids || this.ids.length === 0) {
|
||||
this.$message.warning('未选择要打印的资产')
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
printAssetLabel(this.ids).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.assetsToPrint = response.data || []
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取打印数据失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 执行打印 */
|
||||
handlePrint() {
|
||||
// 创建打印窗口
|
||||
const printWindow = window.open('', '_blank')
|
||||
|
||||
if (!printWindow) {
|
||||
this.$message.error('打印窗口被阻止,请允许弹出窗口后重试')
|
||||
return
|
||||
}
|
||||
|
||||
// 构建打印内容
|
||||
let printContent = `
|
||||
<html>
|
||||
<head>
|
||||
<title>资产标签打印</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
.print-page {
|
||||
page-break-after: always;
|
||||
}
|
||||
.label-item {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
margin-bottom: 15px;
|
||||
width: 300px;
|
||||
}
|
||||
.label-header {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.label-content {
|
||||
padding: 5px;
|
||||
}
|
||||
.label-field {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.field-label {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
}
|
||||
.field-value {
|
||||
display: inline-block;
|
||||
}
|
||||
.label-qrcode, .label-barcode {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.mock-qrcode {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: repeating-conic-gradient(#000 0% 25%, #fff 0% 50%) 50% / 10px 10px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.mock-barcode {
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
background: repeating-linear-gradient(to right, #000, #000 2px, #fff 2px, #fff 4px);
|
||||
margin: 0 auto;
|
||||
}
|
||||
@media print {
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`
|
||||
|
||||
this.assetsToPrint.forEach((asset, index) => {
|
||||
printContent += `
|
||||
<div class="label-item">
|
||||
<div class="label-header">资产标签</div>
|
||||
<div class="label-content">
|
||||
${this.printParams.showFields.includes('code') ? `
|
||||
<div class="label-field">
|
||||
<span class="field-label">资产编码:</span>
|
||||
<span class="field-value">${asset.code}</span>
|
||||
</div>
|
||||
` : ''}
|
||||
${this.printParams.showFields.includes('name') ? `
|
||||
<div class="label-field">
|
||||
<span class="field-label">资产名称:</span>
|
||||
<span class="field-value">${asset.name}</span>
|
||||
</div>
|
||||
` : ''}
|
||||
${this.printParams.showFields.includes('qrCode') ? `
|
||||
<div class="label-qrcode">
|
||||
<div class="mock-qrcode"></div>
|
||||
</div>
|
||||
` : ''}
|
||||
${this.printParams.showFields.includes('barCode') ? `
|
||||
<div class="label-barcode">
|
||||
<div class="mock-barcode"></div>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
${(index + 1) % 6 === 0 ? '<div class="print-page"></div>' : ''}
|
||||
`
|
||||
})
|
||||
|
||||
printContent += `
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
// 写入打印窗口
|
||||
printWindow.document.open()
|
||||
printWindow.document.write(printContent)
|
||||
printWindow.document.close()
|
||||
|
||||
// 等待资源加载完成后打印
|
||||
printWindow.onload = function() {
|
||||
printWindow.print()
|
||||
printWindow.close()
|
||||
}
|
||||
},
|
||||
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.$emit('close')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.asset-print-container {
|
||||
padding: 20px;
|
||||
|
||||
.print-options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.option-item {
|
||||
margin-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.option-label {
|
||||
margin-right: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #eee;
|
||||
padding: 15px;
|
||||
border-radius: 5px;
|
||||
|
||||
.preview-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.label-item {
|
||||
width: 300px;
|
||||
margin: 0 15px 15px 0;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
|
||||
.label-header {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.label-content {
|
||||
padding: 5px;
|
||||
|
||||
.label-field {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.field-label {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.field-value {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.label-qrcode, .label-barcode {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mock-qrcode {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: repeating-conic-gradient(#000 0% 25%, #fff 0% 50%) 50% / 10px 10px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.mock-barcode {
|
||||
width: 150px;
|
||||
height: 40px;
|
||||
background: repeating-linear-gradient(to right, #000, #000 2px, #fff 2px, #fff 4px);
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.print-actions {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
664
pc/src/views/asset/inventory/index.vue
Normal file
664
pc/src/views/asset/inventory/index.vue
Normal file
@ -0,0 +1,664 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>资产清单</span>
|
||||
<el-button-group style="float: right;">
|
||||
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd">新增资产</el-button>
|
||||
<el-button type="primary" icon="el-icon-upload2" size="mini" @click="handleImport">批量导入</el-button>
|
||||
<el-button type="primary" icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
|
||||
<el-button type="primary" icon="el-icon-printer" size="mini" @click="handlePrint" :disabled="multiple">打印标签</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
|
||||
<!-- 搜索条件 -->
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="68px">
|
||||
<el-form-item>
|
||||
<el-input v-model="queryParams.keyword" placeholder="输入名称/编码/分类/品牌/位置" style="width: 300px" clearable @keyup.enter.native="handleQuery">
|
||||
<el-button slot="append" icon="el-icon-search" @click="handleQuery"></el-button>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="text" @click="toggleAdvanced">
|
||||
{{ advanced ? '收起' : '展开' }}
|
||||
<i :class="advanced ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-dropdown @command="handleColumnDisplay" split-button type="primary" size="mini">
|
||||
列显示
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item v-for="(item, index) in columns" :key="index" :command="index">
|
||||
<el-checkbox v-model="item.visible">{{ item.label }}</el-checkbox>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 高级搜索条件,展开时显示 -->
|
||||
<el-form v-show="advanced" :model="queryParams" ref="advancedQueryForm" :inline="true" label-width="100px">
|
||||
<el-form-item label="资产状态" prop="status">
|
||||
<el-select v-model="queryParams.status" placeholder="请选择资产状态" clearable style="width: 180px">
|
||||
<el-option v-for="dict in statusOptions" :key="dict.value" :label="dict.label" :value="dict.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="资产分类" prop="classificationId">
|
||||
<el-cascader
|
||||
v-model="queryParams.classificationId"
|
||||
:options="classificationOptions"
|
||||
:props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false }"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="资产名称" prop="name">
|
||||
<el-input v-model="queryParams.name" placeholder="请输入资产名称" clearable style="width: 180px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所在位置" prop="locationId">
|
||||
<el-cascader
|
||||
v-model="queryParams.locationId"
|
||||
:options="locationOptions"
|
||||
:props="{ checkStrictly: true, value: 'id', label: 'name', emitPath: false }"
|
||||
clearable
|
||||
style="width: 180px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="资产编码" prop="code">
|
||||
<el-input v-model="queryParams.code" placeholder="请输入资产编码" clearable style="width: 180px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="设备序列号" prop="serialNumber">
|
||||
<el-input v-model="queryParams.serialNumber" placeholder="请输入设备序列号" clearable style="width: 180px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="管理员" prop="administratorId">
|
||||
<el-select
|
||||
v-model="queryParams.administratorId"
|
||||
placeholder="请选择管理员"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="remoteSearchAdmin"
|
||||
:loading="adminLoading"
|
||||
style="width: 180px"
|
||||
multiple
|
||||
>
|
||||
<el-option
|
||||
v-for="item in adminOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="保养到期" prop="maintenanceExpired">
|
||||
<el-select v-model="queryParams.maintenanceExpired" placeholder="请选择" clearable style="width: 180px">
|
||||
<el-option label="全部" value="" />
|
||||
<el-option label="是" value="1" />
|
||||
<el-option label="否" value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 列表展示区域 -->
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="assetList"
|
||||
border
|
||||
@selection-change="handleSelectionChange"
|
||||
style="width: 100%; margin-top: 15px;"
|
||||
>
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="资产编码" prop="code" width="120" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="primary" @click="handleDetail(scope.row)">{{ scope.row.code }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标签链接" width="100" v-if="columns[1].visible" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" icon="el-icon-printer" @click="handlePrintSingle(scope.row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="资产分类" prop="classificationName" width="120" v-if="columns[2].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="资产名称" prop="name" width="120" v-if="columns[3].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="资产状态" prop="status" width="100" v-if="columns[4].visible">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="getStatusType(scope.row.status)">{{ getStatusText(scope.row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="品牌" prop="brand" width="100" v-if="columns[5].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="型号" prop="model" width="100" v-if="columns[6].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="设备序列号" prop="serialNumber" width="140" v-if="columns[7].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="管理员" prop="administratorName" width="100" v-if="columns[8].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所属公司" prop="companyName" width="140" v-if="columns[9].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="所在位置" prop="locationName" width="140" v-if="columns[10].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="购置时间" prop="purchaseTime" width="120" v-if="columns[11].visible" />
|
||||
<el-table-column label="购置方式" prop="purchaseType" width="100" v-if="columns[12].visible">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.purchaseType === '1' ? '采购' : '租赁' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="购置金额(含税)" prop="purchaseAmount" width="130" v-if="columns[13].visible" />
|
||||
<el-table-column label="入库时间" prop="storageTime" width="120" v-if="columns[14].visible" />
|
||||
<el-table-column label="预计使用期限(月)" prop="expectedUsePeriod" width="150" v-if="columns[15].visible" />
|
||||
<el-table-column label="备注" prop="remark" width="150" v-if="columns[16].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="资产照片" width="100" v-if="columns[17].visible" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-image
|
||||
v-if="scope.row.imageUrl"
|
||||
:src="scope.row.imageUrl"
|
||||
style="width: 50px; height: 50px"
|
||||
:preview-src-list="[scope.row.imageUrl]">
|
||||
</el-image>
|
||||
<span v-else>无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="保养到期时间" prop="maintenanceTime" width="130" v-if="columns[18].visible" />
|
||||
<el-table-column label="保养说明" prop="maintenanceDescription" width="150" v-if="columns[19].visible" :show-overflow-tooltip="true" />
|
||||
<el-table-column label="预计折旧期限(月)" prop="expectedDepreciationPeriod" width="150" v-if="columns[20].visible" />
|
||||
<el-table-column label="操作" width="150" align="center" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="handleUpdate(scope.row)">修改</el-button>
|
||||
<el-button type="text" size="mini" @click="handleDelete(scope.row)" v-if="scope.row.status === '0'" style="color: #F56C6C;">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="queryParams.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="queryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
style="margin-top: 15px; text-align: right;">
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<el-dialog :title="importTitle" :visible.sync="importOpen" width="500px" append-to-body>
|
||||
<el-upload
|
||||
ref="upload"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="upload.headers"
|
||||
:action="upload.url"
|
||||
:disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress"
|
||||
:on-success="handleFileSuccess"
|
||||
:auto-upload="false"
|
||||
drag>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<el-button type="text" @click="downloadImportTemplate">下载模板</el-button>
|
||||
<div>只允许导入xls、xlsx格式文件。</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitFileForm">确 定</el-button>
|
||||
<el-button @click="importOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看详情对话框 -->
|
||||
<el-dialog :title="detailTitle" :visible.sync="detailOpen" width="900px" append-to-body>
|
||||
<asset-detail v-if="detailOpen" :asset-id="currentAssetId" />
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加/修改资产对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
|
||||
<asset-form
|
||||
v-if="open"
|
||||
:asset-id="currentAssetId"
|
||||
:is-edit="isEdit"
|
||||
@refresh="getList"
|
||||
@close="open = false"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 打印标签对话框 -->
|
||||
<el-dialog title="打印资产标签" :visible.sync="printOpen" width="800px" append-to-body>
|
||||
<asset-label-print
|
||||
v-if="printOpen"
|
||||
:ids="ids"
|
||||
@close="printOpen = false"
|
||||
/>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listAsset,
|
||||
getAsset,
|
||||
delAsset,
|
||||
delAssetBatch,
|
||||
exportAsset,
|
||||
downloadTemplate,
|
||||
importAsset,
|
||||
listAssetClassTree,
|
||||
listAssetLocationTree,
|
||||
listCompanies,
|
||||
listUsers,
|
||||
printAssetLabel,
|
||||
getAssetStatusOptions
|
||||
} from '@/api/asset/inventory'
|
||||
import AssetDetail from './components/AssetDetail'
|
||||
import AssetForm from './components/AssetForm'
|
||||
import AssetLabelPrint from './components/AssetLabelPrint'
|
||||
|
||||
export default {
|
||||
name: 'AssetInventory',
|
||||
components: {
|
||||
AssetDetail,
|
||||
AssetForm,
|
||||
AssetLabelPrint
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
advanced: false,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 资产表格数据
|
||||
assetList: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 是否显示详情弹出层
|
||||
detailOpen: false,
|
||||
// 是否显示导入弹出层
|
||||
importOpen: false,
|
||||
// 是否显示打印弹出层
|
||||
printOpen: false,
|
||||
// 导入标题
|
||||
importTitle: '导入资产数据',
|
||||
// 详情标题
|
||||
detailTitle: '资产详情',
|
||||
// 当前处理的资产ID
|
||||
currentAssetId: null,
|
||||
// 是否编辑模式
|
||||
isEdit: false,
|
||||
// 资产状态选项
|
||||
statusOptions: [],
|
||||
// 管理员选项
|
||||
adminOptions: [],
|
||||
// 管理员搜索加载中
|
||||
adminLoading: false,
|
||||
// 资产分类选项
|
||||
classificationOptions: [],
|
||||
// 资产位置选项
|
||||
locationOptions: [],
|
||||
// 公司选项
|
||||
companyOptions: [],
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: '',
|
||||
classificationId: undefined,
|
||||
name: '',
|
||||
locationId: undefined,
|
||||
code: '',
|
||||
serialNumber: '',
|
||||
administratorId: undefined,
|
||||
maintenanceExpired: ''
|
||||
},
|
||||
// 上传参数
|
||||
upload: {
|
||||
// 是否禁用上传
|
||||
isUploading: false,
|
||||
// 设置上传的请求头部
|
||||
headers: { },
|
||||
// 上传的URL
|
||||
url: process.env.VUE_APP_BASE_API + '/asset/inventory/import'
|
||||
},
|
||||
// 列显示设置
|
||||
columns: [
|
||||
{ label: '资产编码', visible: true, disabled: true },
|
||||
{ label: '标签链接', visible: true },
|
||||
{ label: '资产分类', visible: true },
|
||||
{ label: '资产名称', visible: true },
|
||||
{ label: '资产状态', visible: true },
|
||||
{ label: '品牌', visible: true },
|
||||
{ label: '型号', visible: true },
|
||||
{ label: '设备序列号', visible: true },
|
||||
{ label: '管理员', visible: true },
|
||||
{ label: '所属公司', visible: true },
|
||||
{ label: '所在位置', visible: true },
|
||||
{ label: '购置时间', visible: true },
|
||||
{ label: '购置方式', visible: true },
|
||||
{ label: '购置金额(含税)', visible: true },
|
||||
{ label: '入库时间', visible: true },
|
||||
{ label: '预计使用期限(月)', visible: true },
|
||||
{ label: '备注', visible: true },
|
||||
{ label: '资产照片', visible: true },
|
||||
{ label: '保养到期时间', visible: true },
|
||||
{ label: '保养说明', visible: true },
|
||||
{ label: '预计折旧期限(月)', visible: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getStatusOptions()
|
||||
this.getClassificationOptions()
|
||||
this.getLocationOptions()
|
||||
this.getCompanyOptions()
|
||||
},
|
||||
methods: {
|
||||
/** 查询资产列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
listAsset(this.queryParams).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.assetList = response.data.list || []
|
||||
this.total = response.data.total
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取资产列表失败')
|
||||
this.assetList = []
|
||||
this.total = 0
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取资产状态选项 */
|
||||
getStatusOptions() {
|
||||
getAssetStatusOptions().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.statusOptions = response.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取资产分类树形选项 */
|
||||
getClassificationOptions() {
|
||||
listAssetClassTree().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.classificationOptions = response.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取资产位置树形选项 */
|
||||
getLocationOptions() {
|
||||
listAssetLocationTree().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.locationOptions = response.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取公司选项 */
|
||||
getCompanyOptions() {
|
||||
listCompanies().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.companyOptions = response.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 远程搜索管理员 */
|
||||
remoteSearchAdmin(query) {
|
||||
if (query) {
|
||||
this.adminLoading = true
|
||||
listUsers({ name: query }).then(response => {
|
||||
this.adminLoading = false
|
||||
if (response.code === '000000') {
|
||||
this.adminOptions = response.data || []
|
||||
}
|
||||
}).catch(() => {
|
||||
this.adminLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/** 获取资产状态显示文本 */
|
||||
getStatusText(status) {
|
||||
const statusMap = {
|
||||
'0': '空闲',
|
||||
'1': '在用',
|
||||
'2': '借用',
|
||||
'3': '派发中',
|
||||
'4': '退库中',
|
||||
'5': '借出中',
|
||||
'6': '归还中',
|
||||
'7': '维修中'
|
||||
}
|
||||
return statusMap[status] || '未知'
|
||||
},
|
||||
|
||||
/** 获取资产状态显示类型 */
|
||||
getStatusType(status) {
|
||||
const typeMap = {
|
||||
'0': 'success',
|
||||
'1': 'primary',
|
||||
'2': 'info',
|
||||
'3': 'warning',
|
||||
'4': 'warning',
|
||||
'5': 'warning',
|
||||
'6': 'warning',
|
||||
'7': 'danger'
|
||||
}
|
||||
return typeMap[status] || 'info'
|
||||
},
|
||||
|
||||
/** 切换高级搜索 */
|
||||
toggleAdvanced() {
|
||||
this.advanced = !this.advanced
|
||||
},
|
||||
|
||||
/** 处理列显示设置 */
|
||||
handleColumnDisplay(index) {
|
||||
// 资产编码列是必须显示的,不能取消
|
||||
if (index !== 0) {
|
||||
this.columns[index].visible = !this.columns[index].visible
|
||||
}
|
||||
},
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 重置按钮操作 */
|
||||
resetQuery() {
|
||||
this.resetForm('queryForm')
|
||||
if (this.advanced) {
|
||||
this.resetForm('advancedQueryForm')
|
||||
}
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
keyword: '',
|
||||
status: '',
|
||||
classificationId: undefined,
|
||||
name: '',
|
||||
locationId: undefined,
|
||||
code: '',
|
||||
serialNumber: '',
|
||||
administratorId: undefined,
|
||||
maintenanceExpired: ''
|
||||
}
|
||||
this.handleQuery()
|
||||
},
|
||||
|
||||
/** 多选框选中数据 */
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.open = true
|
||||
this.title = '新增资产'
|
||||
this.isEdit = false
|
||||
this.currentAssetId = null
|
||||
},
|
||||
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.open = true
|
||||
this.title = '修改资产'
|
||||
this.isEdit = true
|
||||
this.currentAssetId = row.id
|
||||
},
|
||||
|
||||
/** 详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
this.detailOpen = true
|
||||
this.detailTitle = '资产详情'
|
||||
this.currentAssetId = row.id
|
||||
},
|
||||
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const assetId = row.id
|
||||
this.$confirm('是否确认删除该资产?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delAsset(assetId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(response.msg || '删除失败')
|
||||
}
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
/** 导出按钮操作 */
|
||||
handleExport() {
|
||||
const queryParams = { ...this.queryParams }
|
||||
// 移除分页参数
|
||||
delete queryParams.pageNum
|
||||
delete queryParams.pageSize
|
||||
|
||||
this.$confirm('是否确认导出所有资产数据项?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.exportLoading = true
|
||||
return exportAsset(queryParams)
|
||||
}).then(response => {
|
||||
this.download(response, '资产清单_' + new Date().getTime() + '.xlsx')
|
||||
this.exportLoading = false
|
||||
}).catch(() => {
|
||||
this.exportLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 导入按钮操作 */
|
||||
handleImport() {
|
||||
this.importOpen = true
|
||||
},
|
||||
|
||||
/** 下载导入模板 */
|
||||
downloadImportTemplate() {
|
||||
downloadTemplate().then(response => {
|
||||
this.download(response, 'property_import.xlsx')
|
||||
})
|
||||
},
|
||||
|
||||
/** 文件上传中处理 */
|
||||
handleFileUploadProgress() {
|
||||
this.upload.isUploading = true
|
||||
},
|
||||
|
||||
/** 文件上传成功处理 */
|
||||
handleFileSuccess(response) {
|
||||
this.upload.isUploading = false
|
||||
this.$refs.upload.clearFiles()
|
||||
this.importOpen = false
|
||||
if (response.code === '000000') {
|
||||
this.$message.success('导入成功')
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(response.msg || '导入失败')
|
||||
}
|
||||
},
|
||||
|
||||
/** 提交上传文件 */
|
||||
submitFileForm() {
|
||||
this.$refs.upload.submit()
|
||||
},
|
||||
|
||||
/** 打印标签按钮操作 */
|
||||
handlePrint() {
|
||||
if (this.ids.length === 0) {
|
||||
this.$message.warning('请先选择要打印的资产')
|
||||
return
|
||||
}
|
||||
this.printOpen = true
|
||||
},
|
||||
|
||||
/** 打印单个资产标签 */
|
||||
handlePrintSingle(row) {
|
||||
const ids = [row.id]
|
||||
printAssetLabel(ids).then(response => {
|
||||
if (response.code === '000000') {
|
||||
// 根据实际情况处理打印标签的逻辑
|
||||
this.$message.success('打印标签成功')
|
||||
} else {
|
||||
this.$message.error(response.msg || '打印标签失败')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 分页大小改变 */
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 分页页码改变 */
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
.el-table {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.el-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.el-checkbox + .el-checkbox {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
718
pc/src/views/asset/labelPage/index.vue
Normal file
718
pc/src/views/asset/labelPage/index.vue
Normal file
@ -0,0 +1,718 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>资产标签设置</span>
|
||||
</div>
|
||||
|
||||
<div class="label-container">
|
||||
<!-- 左侧模板选择 -->
|
||||
<div class="template-selection">
|
||||
<div class="section-title">选择模板</div>
|
||||
<div class="template-list">
|
||||
<div v-for="(template, index) in templates" :key="index"
|
||||
:class="['template-item', selectedTemplateIndex === index ? 'selected' : '']"
|
||||
@click="selectTemplate(index)">
|
||||
<div class="template-preview">
|
||||
<div class="qr-preview"></div>
|
||||
<div v-if="index <= 1" class="logo-indicator">支持Logo</div>
|
||||
</div>
|
||||
<div class="template-name">模板{{ index + 1 }}</div>
|
||||
<div class="template-limit">{{ getTemplateLimitText(index) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 中间预览区 -->
|
||||
<div class="preview-area">
|
||||
<div class="preview-content" ref="previewContent">
|
||||
<!-- 预览标签内容 -->
|
||||
<div class="label-preview-card">
|
||||
<div class="qr-code-container">
|
||||
<img src="@/assets/images/qrcode.svg" alt="QR Code" class="qr-image" />
|
||||
<div class="asset-info">
|
||||
<div class="asset-icon" v-if="selectedTemplateIndex <= 1"><i class="el-icon-coin"></i> 资产</div>
|
||||
<div class="label-fields">
|
||||
<div v-for="(field, index) in selectedTemplateFields" :key="index" class="label-field-item">
|
||||
<span class="field-name">{{ field.name }}{{ index === 1 ? field.name.length > 2 ? '' : ' ' : ''
|
||||
}}:</span>
|
||||
<span class="field-value">{{ field.value || 'XXXXXXXXXX' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="label-footer">预览效果:模板{{ selectedTemplateIndex + 1 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧编辑区 -->
|
||||
<div class="edit-area">
|
||||
<div class="section-title">编辑标签</div>
|
||||
|
||||
<div class="fields-section">
|
||||
<div v-if="isFieldsLimitExceeded" class="limit-warning">
|
||||
<i class="el-icon-warning"></i>
|
||||
当前模板最多支持{{ getMaxFieldsCount() }}个字段
|
||||
</div>
|
||||
<div class="field-selection">
|
||||
<el-checkbox-group v-model="selectedFields" @change="handleFieldsChange">
|
||||
<el-checkbox v-for="field in availableFields" :key="field.id" :label="field.id">
|
||||
{{ field.name }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section-title" v-if="selectedTemplateIndex <= 1">设置Logo</div>
|
||||
|
||||
<!-- Logo上传组件,仅模板1和2显示 -->
|
||||
<div v-if="selectedTemplateIndex <= 1" class="setting-group logo-upload-group">
|
||||
<div class="setting-content">
|
||||
<el-upload class="logo-uploader" action="" :http-request="handleLogoUpload" :show-file-list="false"
|
||||
:before-upload="beforeLogoUpload">
|
||||
<img v-if="logoUrl" :src="logoUrl" class="logo-image">
|
||||
<i v-else class="el-icon-plus logo-uploader-icon"></i>
|
||||
</el-upload>
|
||||
<div class="upload-tip">建议上传尺寸为720*280像素且不大于2M的jpg或png图片</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-title">打印设置</div>
|
||||
|
||||
<div class="settings-section">
|
||||
<div class="setting-group">
|
||||
<div class="setting-content">
|
||||
<el-radio-group v-model="paperType" @change="handlePaperTypeChange">
|
||||
<el-radio :label="1">标签专用纸</el-radio>
|
||||
<el-radio :label="2">A4(2列)</el-radio>
|
||||
<el-radio :label="3">A4(3列)</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-group" v-if="paperType === 1">
|
||||
<div class="setting-label">标签宽度(mm):</div>
|
||||
<div class="setting-content">
|
||||
<el-input v-model="labelWidth" type="number" placeholder="请输入宽度"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-group" v-if="paperType === 1">
|
||||
<div class="setting-label">标签高度(mm):</div>
|
||||
<div class="setting-content">
|
||||
<el-input v-model="labelHeight" type="number" placeholder="请输入高度"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="button-group">
|
||||
<el-button type="primary" @click="saveTemplate">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listLabelTemplates, getLabelTemplate, addLabelTemplate, updateLabelTemplate,
|
||||
deleteLabelTemplate, setDefaultTemplate, getLabelFields, previewLabel
|
||||
} from '@/api/asset/label'
|
||||
|
||||
export default {
|
||||
name: 'AssetLabel',
|
||||
data() {
|
||||
return {
|
||||
// 模板列表
|
||||
templates: [
|
||||
{ id: 1, name: '模板1', default: true, maxFields: 1, supportLogo: true },
|
||||
{ id: 2, name: '模板2', default: false, maxFields: 2, supportLogo: true },
|
||||
{ id: 3, name: '模板3', default: false, maxFields: 3, supportLogo: false },
|
||||
{ id: 4, name: '模板4', default: false, maxFields: 4, supportLogo: false }
|
||||
],
|
||||
// 当前选中的模板索引
|
||||
selectedTemplateIndex: 0,
|
||||
// 可用的字段列表
|
||||
availableFields: [
|
||||
{ id: 'assetNo', name: '资产编号' },
|
||||
{ id: 'deviceNo', name: '设备序列号' },
|
||||
{ id: 'assetName', name: '资产名称' },
|
||||
{ id: 'brand', name: '品牌' },
|
||||
{ id: 'model', name: '规格型号' },
|
||||
{ id: 'manager', name: '管理员' },
|
||||
{ id: 'location', name: '位置' },
|
||||
{ id: 'purchaseTime', name: '购买时间' }
|
||||
],
|
||||
// 当前选中的字段
|
||||
selectedFields: ['assetNo'],
|
||||
// 当前模板中的字段及预览值
|
||||
selectedTemplateFields: [
|
||||
{ id: 'assetNo', name: '资产编号', value: 'ASSET-001' }
|
||||
],
|
||||
// 标签设置
|
||||
paperType: 1,
|
||||
labelWidth: 80,
|
||||
labelHeight: 40,
|
||||
// Logo URL
|
||||
logoUrl: '',
|
||||
// 字段数量超出标志
|
||||
isFieldsLimitExceeded: false,
|
||||
// 加载状态
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTemplateList()
|
||||
this.getFieldOptions()
|
||||
},
|
||||
methods: {
|
||||
// 获取模板列表
|
||||
async getTemplateList() {
|
||||
try {
|
||||
this.loading = true
|
||||
// 实际应用中应从API获取
|
||||
// const res = await listLabelTemplates()
|
||||
// this.templates = res.data || []
|
||||
this.loading = false
|
||||
} catch (error) {
|
||||
console.error('获取模板列表失败', error)
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 获取可用字段选项
|
||||
async getFieldOptions() {
|
||||
try {
|
||||
// 实际应用中应从API获取
|
||||
// const res = await getLabelFields()
|
||||
// this.availableFields = res.data || []
|
||||
} catch (error) {
|
||||
console.error('获取字段选项失败', error)
|
||||
}
|
||||
},
|
||||
|
||||
// 获取模板限制文字说明
|
||||
getTemplateLimitText(index) {
|
||||
if (index === 0) {
|
||||
return '支持1个字段';
|
||||
} else if (index === 1) {
|
||||
return '支持最多2个字段';
|
||||
} else if (index === 2) {
|
||||
return '支持最多3个字段';
|
||||
} else {
|
||||
return '支持最多4个字段';
|
||||
}
|
||||
},
|
||||
|
||||
// 获取当前模板最大字段数
|
||||
getMaxFieldsCount() {
|
||||
return this.templates[this.selectedTemplateIndex].maxFields;
|
||||
},
|
||||
|
||||
// 选择模板
|
||||
selectTemplate(index) {
|
||||
this.selectedTemplateIndex = index
|
||||
|
||||
// 清空logo(如果切换到不支持logo的模板)
|
||||
if (index > 1) {
|
||||
this.logoUrl = '';
|
||||
}
|
||||
|
||||
// 模拟选择模板
|
||||
if (index === 0) {
|
||||
this.selectedFields = this.selectedFields.slice(0, 1); // 限制为1个字段
|
||||
this.paperType = 1;
|
||||
this.labelWidth = 80;
|
||||
this.labelHeight = 40;
|
||||
} else if (index === 1) {
|
||||
this.selectedFields = this.selectedFields.slice(0, 2); // 限制为2个字段
|
||||
this.paperType = 2;
|
||||
this.labelWidth = 100;
|
||||
this.labelHeight = 50;
|
||||
} else if (index === 2) {
|
||||
this.selectedFields = this.selectedFields.slice(0, 3); // 限制为3个字段
|
||||
this.paperType = 2;
|
||||
this.labelWidth = 90;
|
||||
this.labelHeight = 60;
|
||||
} else {
|
||||
this.selectedFields = this.selectedFields.slice(0, 4); // 限制为4个字段
|
||||
this.paperType = 3;
|
||||
this.labelWidth = 70;
|
||||
this.labelHeight = 30;
|
||||
}
|
||||
|
||||
this.updateTemplateFields()
|
||||
this.checkFieldsLimit()
|
||||
},
|
||||
|
||||
// 更新模板字段
|
||||
updateTemplateFields() {
|
||||
this.selectedTemplateFields = this.selectedFields.map(fieldId => {
|
||||
const field = this.availableFields.find(f => f.id === fieldId)
|
||||
return {
|
||||
id: fieldId,
|
||||
name: field ? field.name : '',
|
||||
value: this.getFieldSampleValue(fieldId)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 检查字段数量限制
|
||||
checkFieldsLimit() {
|
||||
const maxFields = this.getMaxFieldsCount();
|
||||
this.isFieldsLimitExceeded = this.selectedFields.length > maxFields;
|
||||
|
||||
// 如果超出限制,自动截取符合数量的字段
|
||||
if (this.isFieldsLimitExceeded) {
|
||||
this.selectedFields = this.selectedFields.slice(0, maxFields);
|
||||
this.updateTemplateFields();
|
||||
this.isFieldsLimitExceeded = false;
|
||||
this.$message({
|
||||
message: `当前模板最多支持${maxFields}个字段,已自动调整`,
|
||||
type: 'warning'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 获取字段示例值
|
||||
getFieldSampleValue(fieldId) {
|
||||
const sampleValues = {
|
||||
'assetNo': 'ASSET-001',
|
||||
'deviceNo': 'SN12345678',
|
||||
'assetName': '办公电脑',
|
||||
'brand': 'Dell',
|
||||
'model': 'Latitude 7400',
|
||||
'manager': '张三',
|
||||
'location': '研发部-A区',
|
||||
'purchaseTime': '2023-01-15'
|
||||
}
|
||||
return sampleValues[fieldId] || ''
|
||||
},
|
||||
|
||||
// 纸张类型变更
|
||||
handlePaperTypeChange(type) {
|
||||
// 根据纸张类型设置默认尺寸
|
||||
if (type === 1) {
|
||||
this.labelWidth = 80
|
||||
this.labelHeight = 40
|
||||
} else if (type === 2) {
|
||||
this.labelWidth = 100
|
||||
this.labelHeight = 50
|
||||
} else if (type === 3) {
|
||||
this.labelWidth = 70
|
||||
this.labelHeight = 30
|
||||
}
|
||||
},
|
||||
|
||||
// 字段选择变更
|
||||
handleFieldsChange() {
|
||||
this.checkFieldsLimit();
|
||||
this.updateTemplateFields();
|
||||
},
|
||||
|
||||
// Logo上传前校验
|
||||
beforeLogoUpload(file) {
|
||||
const isImageType = file.type === 'image/jpeg' || file.type === 'image/png';
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
|
||||
if (!isImageType) {
|
||||
this.$message.error('上传图片只能是 JPG 或 PNG 格式!');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isLt2M) {
|
||||
this.$message.error('上传图片大小不能超过 2MB!');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
// 处理Logo上传
|
||||
handleLogoUpload(options) {
|
||||
const file = options.file;
|
||||
// 实际使用时应该调用上传API
|
||||
// 这里使用FileReader模拟文件上传
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
this.logoUrl = reader.result;
|
||||
};
|
||||
},
|
||||
|
||||
// 保存模板
|
||||
saveTemplate() {
|
||||
if (this.isFieldsLimitExceeded) {
|
||||
this.$message.error(`字段数量超出限制,当前模板最多支持${this.getMaxFieldsCount()}个字段`);
|
||||
return;
|
||||
}
|
||||
|
||||
const templateData = {
|
||||
id: this.templates[this.selectedTemplateIndex].id,
|
||||
name: this.templates[this.selectedTemplateIndex].name,
|
||||
paperType: this.paperType,
|
||||
width: this.labelWidth,
|
||||
height: this.labelHeight,
|
||||
fields: this.selectedFields,
|
||||
logoUrl: this.logoUrl
|
||||
}
|
||||
|
||||
// 实际应用中应该调用API保存
|
||||
// if (templateData.id) {
|
||||
// updateLabelTemplate(templateData).then(res => {
|
||||
// if (res.code === '000000') {
|
||||
// this.$message.success('模板保存成功')
|
||||
// } else {
|
||||
// this.$message.error(res.msg || '保存失败')
|
||||
// }
|
||||
// })
|
||||
// } else {
|
||||
// addLabelTemplate(templateData).then(res => {
|
||||
// if (res.code === '000000') {
|
||||
// this.$message.success('模板创建成功')
|
||||
// } else {
|
||||
// this.$message.error(res.msg || '创建失败')
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
this.$message.success('模板保存成功')
|
||||
},
|
||||
|
||||
// 重置模板
|
||||
resetTemplate() {
|
||||
this.selectTemplate(this.selectedTemplateIndex)
|
||||
this.logoUrl = ''
|
||||
this.$message.info('已重置为原始设置')
|
||||
},
|
||||
|
||||
// 打印标签
|
||||
printLabel() {
|
||||
if (this.isFieldsLimitExceeded) {
|
||||
this.$message.error(`字段数量超出限制,当前模板最多支持${this.getMaxFieldsCount()}个字段`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.$message.success('打印请求已发送')
|
||||
// 实际应用中应该调用打印API
|
||||
// const printData = {
|
||||
// templateId: this.templates[this.selectedTemplateIndex].id,
|
||||
// assetIds: [], // 这里需要选择具体的资产ID
|
||||
// logoUrl: this.logoUrl
|
||||
// }
|
||||
// printLabel(printData).then(res => {
|
||||
// if (res.code === '000000') {
|
||||
// this.$message.success('打印任务已提交')
|
||||
// } else {
|
||||
// this.$message.error(res.msg || '打印失败')
|
||||
// }
|
||||
// })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.label-container {
|
||||
display: flex;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.template-selection {
|
||||
width: 220px;
|
||||
border-right: 1px solid #ebeef5;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 15px;
|
||||
padding-left: 10px;
|
||||
border-left: 3px solid #409EFF;
|
||||
}
|
||||
|
||||
.template-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.template-item {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
border-color: #c0c4cc;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border-color: #409EFF;
|
||||
box-shadow: 0 0 5px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.template-preview {
|
||||
margin-bottom: 8px;
|
||||
position: relative;
|
||||
|
||||
.qr-preview {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
position: relative;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
&:before {
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 8px;
|
||||
left: 8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-indicator {
|
||||
position: absolute;
|
||||
bottom: -5px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background-color: #409EFF;
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.template-name {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.template-limit {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.preview-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label-preview-card {
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
// width: 300px;
|
||||
margin: 0 auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.qr-code-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.qr-image {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.asset-info {
|
||||
margin-left: 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.asset-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
margin-top: 10px;
|
||||
|
||||
i {
|
||||
margin-right: 5px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.label-fields {
|
||||
margin-top: 5px;
|
||||
|
||||
.label-field-item {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
|
||||
.field-name {
|
||||
min-width: 80px;
|
||||
color: #333;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.field-value {
|
||||
color: #333;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.label-footer {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.edit-area {
|
||||
width: 300px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.settings-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.setting-group {
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.setting-label {
|
||||
width: 110px;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.setting-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.logo-upload-group {
|
||||
align-items: flex-start;
|
||||
|
||||
.logo-uploader {
|
||||
.el-upload {
|
||||
border: 1px dashed #d9d9d9;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
border-color: #409EFF;
|
||||
}
|
||||
}
|
||||
|
||||
.logo-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 120px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo-image {
|
||||
width: 120px;
|
||||
height: 60px;
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-tip {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.fields-section {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.limit-warning {
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
color: #E6A23C;
|
||||
margin-bottom: 5px;
|
||||
|
||||
i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.field-selection {
|
||||
margin-top: 10px;
|
||||
|
||||
.el-checkbox-group {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button-group {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
@ -4,17 +4,25 @@
|
||||
<!-- 左侧树形结构 -->
|
||||
<el-col :span="6">
|
||||
<div class="tree-container">
|
||||
<div class="tree-header">
|
||||
<span class="tree-title">资产位置</span>
|
||||
|
||||
</div>
|
||||
<el-divider class="tree-divider"></el-divider>
|
||||
<el-tree
|
||||
ref="locationTree"
|
||||
:data="locationTree"
|
||||
:props="defaultProps"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
highlight-current
|
||||
@node-click="handleNodeClick"
|
||||
:default-expanded-keys="defaultExpandedKeys">
|
||||
<span class="custom-tree-node" slot-scope="{ node, data }">
|
||||
<span>{{ node.label }}</span>
|
||||
<span v-if="data.children && data.children.length > 0" class="node-count">
|
||||
({{ data.children.length }})
|
||||
</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
@ -26,11 +34,18 @@
|
||||
<div slot="header" class="clearfix">
|
||||
<span>位置列表</span>
|
||||
<el-button
|
||||
style="float: right;"
|
||||
style="float: right; margin-left: 10px;"
|
||||
type="primary"
|
||||
icon="el-icon-plus"
|
||||
size="mini"
|
||||
@click="handleAdd">新增位置</el-button>
|
||||
<el-button
|
||||
style="float: right;"
|
||||
type="danger"
|
||||
icon="el-icon-delete"
|
||||
size="mini"
|
||||
:disabled="multipleSelection.length === 0"
|
||||
@click="handleBatchDelete">批量删除</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 搜索表单 -->
|
||||
@ -53,10 +68,22 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="locationList" border stripe style="width: 100%">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="locationList"
|
||||
border
|
||||
stripe
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="locationCode" label="位置编码" width="150" />
|
||||
<el-table-column prop="locationName" label="位置名称" width="200" />
|
||||
<el-table-column prop="parentName" label="上级位置" width="200" />
|
||||
<el-table-column label="上级位置" width="200">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ getParentName(scope.row.parentId) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column prop="levelCode" label="层级编码" width="120" /> -->
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === '1' ? 'success' : 'info'">
|
||||
@ -108,20 +135,25 @@
|
||||
<el-input v-model="form.locationName" placeholder="请输入位置名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="上级位置" prop="parentId">
|
||||
<el-tree-select
|
||||
<el-cascader
|
||||
v-model="form.parentId"
|
||||
:data="locationTree"
|
||||
:props="defaultProps"
|
||||
placeholder="请选择上级位置"
|
||||
:options="locationTreeOptions"
|
||||
:props="{
|
||||
checkStrictly: true,
|
||||
emitPath: false,
|
||||
expandTrigger: 'hover',
|
||||
value: 'id',
|
||||
label: 'label'
|
||||
}"
|
||||
clearable
|
||||
filterable
|
||||
check-strictly
|
||||
default-expand-all
|
||||
node-key="id"
|
||||
:render-after-expand="false"
|
||||
placeholder="请选择上级位置"
|
||||
style="width: 100%">
|
||||
</el-tree-select>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="层级编码" prop="levelCode">
|
||||
<el-input v-model="form.levelCode" placeholder="请输入层级编码" />
|
||||
</el-form-item> -->
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio label="1">启用</el-radio>
|
||||
@ -141,7 +173,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listLocation, getLocation, addLocation, updateLocation, delLocation, enableLocation, disableLocation, getLocationTree, checkLocationInUse } from '@/api/asset/location'
|
||||
import { listLocation, getLocation, addLocation, updateLocation, delLocation, enableLocation, disableLocation, getLocationTree, checkLocationInUse, getChildLocations, delLocationBatch, checkLocationCode } from '@/api/asset/location'
|
||||
|
||||
export default {
|
||||
name: 'AssetLocation',
|
||||
@ -155,12 +187,14 @@ export default {
|
||||
locationList: [],
|
||||
// 位置树形数据
|
||||
locationTree: [],
|
||||
// 处理后的树形选项数据(用于级联选择器)
|
||||
locationTreeOptions: [],
|
||||
// 默认展开的节点
|
||||
defaultExpandedKeys: [],
|
||||
// 树形配置
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'locationName'
|
||||
label: 'label'
|
||||
},
|
||||
// 弹出层标题
|
||||
dialogTitle: '',
|
||||
@ -181,7 +215,8 @@ export default {
|
||||
rules: {
|
||||
locationCode: [
|
||||
{ required: true, message: '请输入位置编码', trigger: 'blur' },
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' }
|
||||
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' },
|
||||
{ validator: this.validateLocationCode, trigger: 'blur' }
|
||||
],
|
||||
locationName: [
|
||||
{ required: true, message: '请输入位置名称', trigger: 'blur' },
|
||||
@ -193,7 +228,9 @@ export default {
|
||||
remark: [
|
||||
{ max: 200, message: '长度不能超过200个字符', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
},
|
||||
// 多选选中项
|
||||
multipleSelection: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -201,15 +238,148 @@ export default {
|
||||
this.getList()
|
||||
},
|
||||
methods: {
|
||||
/** 检验位置编码唯一性 */
|
||||
validateLocationCode(rule, value, callback) {
|
||||
if (value === '') {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
checkLocationCode(this.form.id, value).then(res => {
|
||||
if (res.code === '000000') {
|
||||
if (res.data) {
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error('该位置编码已存在'))
|
||||
}
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}).catch(() => {
|
||||
callback()
|
||||
})
|
||||
},
|
||||
/** 获取位置树形结构 */
|
||||
getTree() {
|
||||
getLocationTree().then(res => {
|
||||
if (res.code === 200) {
|
||||
this.locationTree = res.data
|
||||
// 默认展开第一级节点
|
||||
if (this.locationTree.length > 0) {
|
||||
this.defaultExpandedKeys = [this.locationTree[0].id]
|
||||
this.loading = true
|
||||
const params = {
|
||||
status: undefined // 获取所有状态的位置
|
||||
}
|
||||
getLocationTree(params).then(res => {
|
||||
if (res.code === '000000') {
|
||||
// 处理返回的树形数据,确保字段名称正确
|
||||
this.locationTree = this.processTreeData(res.data)
|
||||
|
||||
// 准备级联选择器的数据源
|
||||
this.locationTreeOptions = this.prepareTreeSelectOptions(this.locationTree)
|
||||
|
||||
// 如果之前已经有展开的节点,保持这些节点展开
|
||||
// 否则只展开第一级有子节点的节点
|
||||
if (!this.defaultExpandedKeys || this.defaultExpandedKeys.length === 0) {
|
||||
this.defaultExpandedKeys = this.getFirstLevelKeys(this.locationTree)
|
||||
}
|
||||
|
||||
// 强制刷新树组件
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.locationTree) {
|
||||
this.$refs.locationTree.$forceUpdate()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取位置树失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error('获取位置树失败', error)
|
||||
this.$message.error('系统错误,获取位置树失败')
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取第一级节点的key,仅展开有子节点的 */
|
||||
getFirstLevelKeys(tree) {
|
||||
if (!tree || !Array.isArray(tree)) return []
|
||||
|
||||
return tree
|
||||
.filter(node => node.children && node.children.length > 0)
|
||||
.map(node => node.id)
|
||||
},
|
||||
|
||||
/** 获取所有节点key,用于全部展开 */
|
||||
getExpandedKeys(tree, keys = []) {
|
||||
for (const node of tree) {
|
||||
if (node.children && node.children.length > 0) {
|
||||
keys.push(node.id)
|
||||
this.getExpandedKeys(node.children, keys)
|
||||
}
|
||||
}
|
||||
return keys
|
||||
},
|
||||
|
||||
/** 处理树形数据,确保字段名称正确 */
|
||||
processTreeData(data) {
|
||||
if (!data || !Array.isArray(data)) return []
|
||||
|
||||
const processNode = (node) => {
|
||||
// 创建一个新对象,使用驼峰命名
|
||||
const processedNode = {
|
||||
id: node.id,
|
||||
// 转换label,格式为"编码-名称"
|
||||
label: `${node.code || ''}-${node.locationName || node.label || ''}`,
|
||||
// 驼峰命名
|
||||
locationName: node.locationName || node.label || '',
|
||||
locationCode: node.code || '',
|
||||
status: node.status || '1',
|
||||
parentId: node.parentId || null,
|
||||
levelCode: node.levelCode || '',
|
||||
children: []
|
||||
}
|
||||
|
||||
// 递归处理子节点
|
||||
if (node.children && Array.isArray(node.children) && node.children.length > 0) {
|
||||
processedNode.children = node.children.map(child => processNode(child))
|
||||
}
|
||||
|
||||
return processedNode
|
||||
}
|
||||
|
||||
return data.map(item => processNode(item))
|
||||
},
|
||||
|
||||
/** 准备级联选择器的数据源 */
|
||||
prepareTreeSelectOptions(data) {
|
||||
if (!data || !Array.isArray(data)) return []
|
||||
|
||||
// 创建一个无上级选项
|
||||
const options = [{
|
||||
id: '',
|
||||
label: '无上级位置',
|
||||
children: []
|
||||
}]
|
||||
|
||||
// 添加所有位置节点
|
||||
const cloneData = JSON.parse(JSON.stringify(data))
|
||||
options.push(...cloneData)
|
||||
|
||||
return options
|
||||
},
|
||||
|
||||
/** 转换下划线命名为驼峰命名 */
|
||||
convertSnakeToCamel(list) {
|
||||
if (!list || !Array.isArray(list)) return []
|
||||
|
||||
return list.map(item => {
|
||||
return {
|
||||
id: item.id,
|
||||
locationCode: item.locationCode || '',
|
||||
locationName: item.locationName || '',
|
||||
parentId: item.parentId || null,
|
||||
levelCode: item.levelCode || '',
|
||||
status: item.status || '1',
|
||||
remark: item.remark || '',
|
||||
createTime: item.createTime || '',
|
||||
createUserId: item.createUserId || '',
|
||||
lastModUserId: item.lastModUserId || '',
|
||||
lastModTime: item.lastModTime || ''
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -217,17 +387,41 @@ export default {
|
||||
getList() {
|
||||
this.loading = true
|
||||
listLocation(this.queryParams).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.locationList = res.data.rows
|
||||
if (res.code === '000000') {
|
||||
// 转换API返回数据结构为驼峰命名
|
||||
this.locationList = this.convertSnakeToCamel(res.data.list)
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取位置列表失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error('获取位置列表失败', error)
|
||||
this.$message.error('系统错误,获取位置列表失败')
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 节点单击事件 */
|
||||
handleNodeClick(data) {
|
||||
this.queryParams.parentId = data.id
|
||||
this.getList()
|
||||
// 使用新的获取子位置API
|
||||
this.loading = true
|
||||
getChildLocations(data.id, {
|
||||
pageNum: this.queryParams.pageNum,
|
||||
pageSize: this.queryParams.pageSize
|
||||
}).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.locationList = this.convertSnakeToCamel(res.data.list)
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取子位置列表失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error('获取子位置列表失败', error)
|
||||
this.$message.error('系统错误,获取子位置列表失败')
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
/** 搜索按钮操作 */
|
||||
handleQuery() {
|
||||
@ -254,8 +448,10 @@ export default {
|
||||
locationCode: '',
|
||||
locationName: '',
|
||||
parentId: this.queryParams.parentId || '', // 如果当前有选中节点,则默认为父节点
|
||||
levelCode: '',
|
||||
status: '1',
|
||||
remark: ''
|
||||
remark: '',
|
||||
createUserId: this.$store.getters.userId || ''
|
||||
}
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
@ -268,12 +464,20 @@ export default {
|
||||
handleEdit(row) {
|
||||
const id = row.id
|
||||
getLocation(id).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.form = res.data
|
||||
// 确保parentId是字符串或数字类型,而不是对象
|
||||
if (this.form.parentId && typeof this.form.parentId === 'object') {
|
||||
this.form.parentId = this.form.parentId.id || ''
|
||||
if (res.code === '000000') {
|
||||
// 将API响应数据映射到表单字段,使用驼峰命名
|
||||
this.form = {
|
||||
id: res.data.id,
|
||||
locationCode: res.data.locationCode,
|
||||
locationName: res.data.locationCode,
|
||||
parentId: res.data.parentId || '',
|
||||
levelCode: res.data.levelCode || '',
|
||||
status: res.data.status,
|
||||
remark: res.data.remark,
|
||||
// 添加最后修改人
|
||||
lastModUserId: this.$store.getters.userId || ''
|
||||
}
|
||||
|
||||
this.dialogTitle = '编辑位置'
|
||||
this.dialogVisible = true
|
||||
// 重置表单验证
|
||||
@ -282,21 +486,57 @@ export default {
|
||||
this.$refs.form.clearValidate()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error(res.msg || '获取位置详情失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取位置详情失败', error)
|
||||
this.$message.error('系统错误,获取位置详情失败')
|
||||
})
|
||||
},
|
||||
/** 提交按钮 */
|
||||
submitForm() {
|
||||
this.$refs['form'].validate(valid => {
|
||||
if (valid) {
|
||||
// A. 构建提交数据,映射表单字段到API字段
|
||||
const submitData = {
|
||||
id: this.form.id,
|
||||
locationCode: this.form.locationCode,
|
||||
locationName: this.form.locationName,
|
||||
parentId: this.form.parentId,
|
||||
levelCode: this.form.levelCode,
|
||||
status: this.form.status,
|
||||
remark: this.form.remark
|
||||
}
|
||||
|
||||
// B. 添加创建/修改用户信息
|
||||
if (this.form.id) {
|
||||
submitData.lastModUserId = this.form.lastModUserId || this.$store.getters.userId || ''
|
||||
} else {
|
||||
submitData.createUserId = this.form.createUserId || this.$store.getters.userId || ''
|
||||
}
|
||||
|
||||
const method = this.form.id ? updateLocation : addLocation
|
||||
method(this.form).then(res => {
|
||||
if (res.code === 200) {
|
||||
method(submitData).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.$message.success('操作成功')
|
||||
this.dialogVisible = false
|
||||
|
||||
// 重新获取树形结构,确保更新多级结构
|
||||
this.getTree()
|
||||
|
||||
// 如果是新增子位置,更新当前列表
|
||||
if (this.queryParams.parentId && !this.form.id) {
|
||||
this.handleNodeClick({ id: this.queryParams.parentId })
|
||||
} else {
|
||||
this.getList()
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.msg || '操作失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('提交表单失败', error)
|
||||
this.$message.error('系统错误,提交失败')
|
||||
})
|
||||
}
|
||||
})
|
||||
@ -310,17 +550,24 @@ export default {
|
||||
}).then(() => {
|
||||
// 先检查位置是否被使用
|
||||
checkLocationInUse(row.id).then(res => {
|
||||
if (res.code === 200 && res.data) {
|
||||
if (res.code === '000000' && res.data) {
|
||||
this.$message.error('该位置已被使用,无法删除')
|
||||
return
|
||||
}
|
||||
// 未被使用,执行删除操作
|
||||
delLocation(row.id).then(res => {
|
||||
if (res.code === 200) {
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
delLocation(row.id, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.$message.success('删除成功')
|
||||
// 重新获取树形结构,确保更新多级结构
|
||||
this.getTree()
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.msg || '删除失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('删除操作失败', error)
|
||||
this.$message.error('系统错误,删除失败')
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
@ -334,13 +581,20 @@ export default {
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 获取当前用户ID
|
||||
const lastModUserId = this.$store.getters.userId || '1'
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
const method = row.status === '1' ? disableLocation : enableLocation
|
||||
method(row.id, lastModUserId).then(res => {
|
||||
if (res.code === 200) {
|
||||
if (res.code === '000000') {
|
||||
this.$message.success(`${action}成功`)
|
||||
// 状态变更后,可能会影响树的显示,所以也需要刷新树
|
||||
this.getTree()
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.msg || `${action}失败`)
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(`${action}操作失败`, error)
|
||||
this.$message.error(`系统错误,${action}失败`)
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
@ -353,6 +607,90 @@ export default {
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
},
|
||||
/** 获取上级位置名称 */
|
||||
getParentName(parentId) {
|
||||
if (!parentId) return '无';
|
||||
|
||||
// 先在树形数据中查找
|
||||
const findParentInTree = (tree, id) => {
|
||||
for (const node of tree) {
|
||||
if (node.id === id) {
|
||||
return node.locationCode + '-' + node.locationName;
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
const found = findParentInTree(node.children, id);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const parentName = findParentInTree(this.locationTree, parentId);
|
||||
if (parentName) return parentName;
|
||||
|
||||
// 如果在树中没找到,可能在列表数据中
|
||||
const parent = this.locationList.find(item => item.id === parentId);
|
||||
return parent ? (parent.locationCode + '-' + parent.locationName) : '未知';
|
||||
},
|
||||
/** 多选选中项改变 */
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
/** 批量删除按钮操作 */
|
||||
handleBatchDelete() {
|
||||
if (this.multipleSelection.length === 0) {
|
||||
this.$message.warning('请至少选择一个位置')
|
||||
return
|
||||
}
|
||||
|
||||
this.$confirm('是否确认删除选中的位置?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 获取选中的位置ID列表
|
||||
const selectedIds = this.multipleSelection.map(item => item.id)
|
||||
// 获取当前用户ID
|
||||
const lastModUserId = this.$store.getters.userId || ''
|
||||
|
||||
// 执行批量删除操作
|
||||
this.loading = true
|
||||
delLocationBatch(selectedIds, lastModUserId).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.$message.success('批量删除成功')
|
||||
// 重新获取树形结构,确保更新多级结构
|
||||
this.getTree()
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(res.msg || '批量删除失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(error => {
|
||||
console.error('批量删除操作失败', error)
|
||||
this.$message.error('系统错误,批量删除失败')
|
||||
this.loading = false
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
/** 展开所有节点 */
|
||||
expandAll() {
|
||||
const allKeys = this.getExpandedKeys(this.locationTree)
|
||||
this.defaultExpandedKeys = allKeys
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.locationTree) {
|
||||
this.$refs.locationTree.$forceUpdate()
|
||||
}
|
||||
})
|
||||
},
|
||||
/** 收起所有节点 */
|
||||
collapseAll() {
|
||||
this.defaultExpandedKeys = []
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.locationTree) {
|
||||
this.$refs.locationTree.$forceUpdate()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -370,6 +708,28 @@ export default {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.tree-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.tree-title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.tree-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tree-divider {
|
||||
margin: 5px 0 15px;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@ -383,6 +743,12 @@ export default {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.node-count {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
text-align: right;
|
||||
@ -412,5 +778,14 @@ export default {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 树节点样式优化 */
|
||||
::v-deep .el-tree-node__content {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
::v-deep .el-tree-node__expand-icon {
|
||||
padding: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
285
pc/src/views/project/room/components/DetailView.vue
Normal file
285
pc/src/views/project/room/components/DetailView.vue
Normal file
@ -0,0 +1,285 @@
|
||||
<template>
|
||||
<div class="detail-container" v-loading="loading">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基础信息" name="basic">
|
||||
<el-descriptions class="margin-top" title="房源基础信息" :column="3" border>
|
||||
<el-descriptions-item label="项目名称">{{ roomDetail.projectName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="楼宇">{{ roomDetail.buildingName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="楼层">{{ roomDetail.floorName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="房号">{{ roomDetail.roomNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="房间数字编号">{{ roomDetail.roomDigitalNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="房源管理编号">{{ roomDetail.roomManageNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="建筑面积">{{ roomDetail.buildingArea }} m²</el-descriptions-item>
|
||||
<el-descriptions-item label="计租面积">{{ roomDetail.rentalArea }} m²</el-descriptions-item>
|
||||
<el-descriptions-item label="计费面积">{{ roomDetail.billingArea }} m²</el-descriptions-item>
|
||||
<el-descriptions-item label="套内面积">{{ roomDetail.innerArea }} m²</el-descriptions-item>
|
||||
<el-descriptions-item label="虚拟房源">{{ roomDetail.isVirtual === '1' ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="房源交付时间">{{ roomDetail.deliveryTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="绑定业主">{{ roomDetail.ownerName }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="招商信息" name="merchant">
|
||||
<el-descriptions class="margin-top" title="招商信息" :column="3" border>
|
||||
<el-descriptions-item label="产权性质">
|
||||
<el-tag :type="roomPropertyTypeTagType">{{ roomPropertyTypeText }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="租赁状态">
|
||||
<el-tag :type="leaseStatusTagType">{{ leaseStatusText }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="房源状态">{{ roomStatusText }}</el-descriptions-item>
|
||||
<el-descriptions-item label="房源类型">{{ roomTypeText }}</el-descriptions-item>
|
||||
<el-descriptions-item label="房源备案号">{{ roomDetail.roomRecordNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="层高">{{ roomDetail.height }} m</el-descriptions-item>
|
||||
<el-descriptions-item label="荷载值">{{ roomDetail.loadValue }}</el-descriptions-item>
|
||||
<el-descriptions-item label="装修情况">{{ decorationStatusText }}</el-descriptions-item>
|
||||
<el-descriptions-item label="标签">
|
||||
<el-tag v-for="tag in roomDetail.tagList" :key="tag.id" size="mini" style="margin-right: 5px">
|
||||
{{ tag.tagName }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="招商状态">
|
||||
<el-tag :type="roomDetail.businessStatus === '1' ? 'primary' : 'info'">
|
||||
{{ roomDetail.businessStatus === '1' ? '招商' : '不招商' }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="可租日期">{{ roomDetail.availableDate }}</el-descriptions-item>
|
||||
<el-descriptions-item label="报价">
|
||||
{{ roomDetail.price }} {{ priceUnitText }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="底价">
|
||||
{{ roomDetail.floorPrice }} {{ floorPriceUnitText }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="房源招商编号">{{ roomDetail.businessNumber }}</el-descriptions-item>
|
||||
<el-descriptions-item label="招商条件">{{ roomDetail.businessCondition || '-' }}</el-descriptions-item>
|
||||
|
||||
</el-descriptions>
|
||||
|
||||
<div class="image-section" v-if="roomDetail.imageUrls && roomDetail.imageUrls.length > 0">
|
||||
<div class="section-title">房源图片</div>
|
||||
<el-carousel :interval="4000" type="card" height="200px">
|
||||
<el-carousel-item v-for="(item, index) in roomDetail.imageUrls" :key="index">
|
||||
<img :src="item" class="carousel-image" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="拓展信息" name="extension">
|
||||
<el-descriptions class="margin-top" title="拓展信息" :column="3" border>
|
||||
<el-descriptions-item label="使用率">{{ roomDetail.usageRate || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="办公格局">{{ roomDetail.officeLayout || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="窗面朝向">{{ roomDetail.windowOrientation || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="免租期">{{ roomDetail.rentFreePeriod || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最短租期">{{ roomDetail.minLeaseTerm || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="工位数量">{{ workstationCountRange }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="image-section" v-if="roomDetail.floorPlanImageUrls && roomDetail.floorPlanImageUrls.length > 0">
|
||||
<div class="section-title">户型图</div>
|
||||
<el-carousel :interval="4000" type="card" height="200px">
|
||||
<el-carousel-item v-for="(item, index) in roomDetail.floorPlanImageUrls" :key="index">
|
||||
<img :src="item" class="carousel-image" />
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
<div class="floor-plan-description" v-if="roomDetail.floorPlanDesc">
|
||||
<div class="section-title">户型图说明</div>
|
||||
<p>{{ roomDetail.floorPlanDesc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getRoom } from '@/api/room'
|
||||
|
||||
export default {
|
||||
name: 'DetailView',
|
||||
props: {
|
||||
roomId: {
|
||||
type: [String, Number],
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
activeTab: 'basic',
|
||||
roomDetail: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
roomPropertyTypeText() {
|
||||
const typeMap = {
|
||||
'1': '自持',
|
||||
'2': '承租',
|
||||
'3': '自持+承租'
|
||||
}
|
||||
return typeMap[this.roomDetail.propertyNature] || '-'
|
||||
},
|
||||
roomPropertyTypeTagType() {
|
||||
const typeMap = {
|
||||
'1': 'success',
|
||||
'2': 'primary',
|
||||
'3': 'warning'
|
||||
}
|
||||
return typeMap[this.roomDetail.propertyNature] || 'info'
|
||||
},
|
||||
leaseStatusText() {
|
||||
const statusMap = {
|
||||
'0': '待租',
|
||||
'1': '已租',
|
||||
'2': '预订'
|
||||
}
|
||||
return statusMap[this.roomDetail.rentalStatus] || '-'
|
||||
},
|
||||
leaseStatusTagType() {
|
||||
const statusMap = {
|
||||
'0': 'success',
|
||||
'1': 'danger',
|
||||
'2': 'warning'
|
||||
}
|
||||
return statusMap[this.roomDetail.rentalStatus] || 'info'
|
||||
},
|
||||
decorationStatusText() {
|
||||
const statusMap = {
|
||||
'1': '精装',
|
||||
'2': '简装',
|
||||
'3': '毛坯',
|
||||
'4': '标准交付',
|
||||
'5': '豪装'
|
||||
}
|
||||
return statusMap[this.roomDetail.decorationStatus] || '-'
|
||||
},
|
||||
roomStatusText() {
|
||||
const statusMap = {
|
||||
'1': '公开',
|
||||
'2': '关闭',
|
||||
'3': '私密'
|
||||
}
|
||||
return statusMap[this.roomDetail.roomStatus] || '-'
|
||||
},
|
||||
roomTypeText() {
|
||||
const typeMap = {
|
||||
'1': '办公',
|
||||
'2': '商业',
|
||||
'3': '仓储',
|
||||
'4': '厂房',
|
||||
'5': '多经',
|
||||
'6': '公寓',
|
||||
'7': '住宅',
|
||||
'8': '其它'
|
||||
}
|
||||
return typeMap[this.roomDetail.roomType] || '-'
|
||||
},
|
||||
priceUnitText() {
|
||||
const unitMap = {
|
||||
'1': '元/㎡/天',
|
||||
'2': '元/㎡/月',
|
||||
'3': '元/月'
|
||||
}
|
||||
return unitMap[this.roomDetail.priceUnit] || ''
|
||||
},
|
||||
floorPriceUnitText() {
|
||||
const unitMap = {
|
||||
'1': '元/㎡/天',
|
||||
'2': '元/㎡/月',
|
||||
'3': '元/月'
|
||||
}
|
||||
return unitMap[this.roomDetail.floorPriceUnit] || ''
|
||||
},
|
||||
workstationCountRange() {
|
||||
if (!this.roomDetail.workstationMin && !this.roomDetail.workstationMax) {
|
||||
return '-'
|
||||
}
|
||||
if (this.roomDetail.workstationMin && !this.roomDetail.workstationMax) {
|
||||
return this.roomDetail.workstationMin + '个'
|
||||
}
|
||||
if (!this.roomDetail.workstationMin && this.roomDetail.workstationMax) {
|
||||
return '不超过' + this.roomDetail.workstationMax + '个'
|
||||
}
|
||||
return this.roomDetail.workstationMin + '~' + this.roomDetail.workstationMax + '个'
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getRoomDetail()
|
||||
},
|
||||
methods: {
|
||||
getRoomDetail() {
|
||||
if (!this.roomId) return
|
||||
|
||||
this.loading = true
|
||||
getRoom(this.roomId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.roomDetail = response.data
|
||||
|
||||
// 处理业务信息
|
||||
if (response.data.businessInfo) {
|
||||
Object.assign(this.roomDetail, response.data.businessInfo)
|
||||
}
|
||||
|
||||
// 处理扩展信息
|
||||
if (response.data.extendInfo) {
|
||||
Object.assign(this.roomDetail, response.data.extendInfo)
|
||||
}
|
||||
|
||||
// 处理图片
|
||||
if (response.data.roomImages && Array.isArray(response.data.roomImages)) {
|
||||
this.roomDetail.imageUrls = response.data.roomImages.map(item => item.imageUrl)
|
||||
}
|
||||
|
||||
// 处理户型图
|
||||
if (response.data.floorPlanImages && Array.isArray(response.data.floorPlanImages)) {
|
||||
this.roomDetail.floorPlanImageUrls = response.data.floorPlanImages.map(item => item.imageUrl)
|
||||
}
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取房源详情失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.detail-container {
|
||||
padding: 10px;
|
||||
|
||||
.margin-top {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: 20px 0 15px;
|
||||
padding-left: 10px;
|
||||
border-left: 3px solid #409EFF;
|
||||
}
|
||||
|
||||
.image-section {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.carousel-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.floor-plan-description {
|
||||
margin-top: 20px;
|
||||
|
||||
p {
|
||||
padding: 0 15px;
|
||||
line-height: 1.6;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
841
pc/src/views/project/room/components/RoomForm.vue
Normal file
841
pc/src/views/project/room/components/RoomForm.vue
Normal file
@ -0,0 +1,841 @@
|
||||
<template>
|
||||
<div class="room-form-container" v-loading="loading">
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px" size="small">
|
||||
<el-tabs v-model="activeTab">
|
||||
<!-- 基础信息 Tab -->
|
||||
<el-tab-pane label="基础信息" name="basic">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="项目" prop="projectId">
|
||||
<el-select v-model="form.projectId" placeholder="请选择项目" style="width: 100%"
|
||||
:disabled="isEdit" @change="handleProjectChange">
|
||||
<el-option v-for="item in projectOptions" :key="item.id" :label="item.projectName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="楼宇" prop="buildingId">
|
||||
<el-select v-model="form.buildingId" placeholder="请选择楼宇" style="width: 100%"
|
||||
:disabled="isEdit" @change="handleBuildingChange">
|
||||
<el-option v-for="item in buildingOptions" :key="item.id" :label="item.buildingName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="楼层" prop="floorId">
|
||||
<el-select v-model="form.floorId" placeholder="请选择楼层" style="width: 100%" :disabled="isEdit">
|
||||
<el-option v-for="item in floorOptions" :key="item.id" :label="item.floorName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房号" prop="roomNumber">
|
||||
<el-input v-model="form.roomNumber" placeholder="请输入房号" :disabled="isEdit" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房间数字编号" prop="roomDigitalNumber">
|
||||
<el-input v-model="form.roomDigitalNumber" placeholder="请输入房间数字编号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房源管理编号" prop="roomManageNumber">
|
||||
<el-input v-model="form.roomManageNumber" placeholder="请输入房源管理编号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="建筑面积" prop="buildingArea">
|
||||
<el-input v-model="form.buildingArea" placeholder="请输入建筑面积">
|
||||
<template slot="append">m²</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="计租面积" prop="rentalArea">
|
||||
<el-input v-model="form.rentalArea" placeholder="请输入计租面积">
|
||||
<template slot="append">m²</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="计费面积" prop="billingArea">
|
||||
<el-input v-model="form.billingArea" placeholder="请输入计费面积">
|
||||
<template slot="append">m²</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="套内面积" prop="innerArea">
|
||||
<el-input v-model="form.innerArea" placeholder="请输入套内面积">
|
||||
<template slot="append">m²</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="虚拟房源" prop="isVirtual">
|
||||
<el-radio-group v-model="form.isVirtual">
|
||||
<el-radio label="1">是</el-radio>
|
||||
<el-radio label="0">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房源交付时间" prop="deliveryTime">
|
||||
<el-date-picker v-model="form.deliveryTime" type="date" placeholder="请选择房源交付时间" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="绑定业主" prop="ownerId">
|
||||
<el-select v-model="form.ownerId" placeholder="请选择业主" filterable remote reserve-keyword
|
||||
:remote-method="remoteSearchOwner" :loading="ownerLoading" style="width: 100%">
|
||||
<el-option v-for="item in ownerOptions" :key="item.id" :label="item.ownerName" :value="item.id">
|
||||
<span style="float: left">{{ item.ownerName }}</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.phone }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 招商信息 Tab -->
|
||||
<el-tab-pane label="招商信息" name="merchant">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="产权性质" prop="propertyNature">
|
||||
<el-select v-model="form.propertyNature" placeholder="请选择产权性质" style="width: 100%" @change="handleInputChange">
|
||||
<el-option label="自持" value="1" />
|
||||
<el-option label="承租" value="2" />
|
||||
<el-option label="自持+承租" value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="租赁状态" prop="rentalStatus">
|
||||
<el-select v-model="form.rentalStatus" placeholder="请选择租赁状态" style="width: 100%" disabled @change="handleInputChange">
|
||||
<el-option label="待租" value="1" />
|
||||
<el-option label="已租" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房源状态" prop="roomStatus">
|
||||
<el-select v-model="form.roomStatus" placeholder="请选择房源状态" style="width: 100%" @change="handleInputChange">
|
||||
<el-option label="公开" value="1" />
|
||||
<el-option label="关闭" value="2" />
|
||||
<el-option label="私密" value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房源类型" prop="roomType">
|
||||
<el-select v-model="form.roomType" placeholder="请选择房源类型" style="width: 100%" @change="handleInputChange">
|
||||
<el-option label="办公" value="1" />
|
||||
<el-option label="商业" value="2" />
|
||||
<el-option label="仓储" value="3" />
|
||||
<el-option label="厂房" value="4" />
|
||||
<el-option label="多经" value="5" />
|
||||
<el-option label="公寓" value="6" />
|
||||
<el-option label="住宅" value="7" />
|
||||
<el-option label="其它" value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房源备案号" prop="roomRecordNumber">
|
||||
<el-input v-model="form.roomRecordNumber" placeholder="请输入房源备案号" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="层高" prop="height">
|
||||
<el-input v-model="form.height" placeholder="请输入层高" @input="handleInputChange">
|
||||
<template slot="append">m</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="荷载值" prop="loadValue">
|
||||
<el-input v-model="form.loadValue" placeholder="请输入荷载值" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房源标签" prop="tagIds">
|
||||
<el-select v-model="form.tagIds" placeholder="请选择房源标签" multiple style="width: 100%" @change="handleInputChange">
|
||||
<el-option v-for="tag in tagOptions" :key="tag.id" :label="tag.tagName" :value="tag.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="招商状态" prop="businessStatus">
|
||||
<el-select v-model="form.businessStatus" placeholder="请选择招商状态" style="width: 100%" @change="handleInputChange">
|
||||
<el-option label="招商" value="1" />
|
||||
<el-option label="不招商" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="可租日期" prop="availableDate">
|
||||
<el-date-picker v-model="form.availableDate" type="date" placeholder="请选择可租日期" style="width: 100%" @change="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="装修情况" prop="decorationStatus">
|
||||
<el-select v-model="form.decorationStatus" placeholder="请选择装修情况" style="width: 100%" @change="handleInputChange">
|
||||
<el-option label="精装" value="1" />
|
||||
<el-option label="简装" value="2" />
|
||||
<el-option label="毛坯" value="3" />
|
||||
<el-option label="标准交付" value="4" />
|
||||
<el-option label="豪装" value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-form-item label="报价" prop="price">
|
||||
<el-input v-model="form.price" placeholder="请输入报价" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-form-item label="底价" prop="floorPrice">
|
||||
<el-input v-model="form.floorPrice" placeholder="请输入底价" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span="8">
|
||||
<el-form-item label="房源招商编号" prop="businessNumber">
|
||||
<el-input v-model="form.businessNumber" placeholder="请输入房源招商编号" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="招商条件" prop="businessCondition">
|
||||
<el-input v-model="form.businessCondition" placeholder="请输入招商条件" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
|
||||
</el-row>
|
||||
<el-form-item label="房源图片" prop="imageUrls">
|
||||
<el-upload
|
||||
:action="uploadUrl"
|
||||
list-type="picture-card"
|
||||
:headers="uploadHeaders"
|
||||
:file-list="imageList"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-success="handleImageSuccess"
|
||||
:on-remove="handleImageRemove"
|
||||
multiple>
|
||||
<i class="el-icon-plus"></i>
|
||||
</el-upload>
|
||||
<el-dialog :visible.sync="dialogVisible">
|
||||
<img width="100%" :src="dialogImageUrl" alt="">
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 拓展信息 Tab -->
|
||||
<el-tab-pane label="拓展信息" name="extension">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="使用率" prop="usageRate">
|
||||
<el-input v-model="form.usageRate" placeholder="例如:2+1" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="办公格局" prop="officeLayout">
|
||||
<el-input v-model="form.officeLayout" placeholder="请输入办公格局" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="窗面朝向" prop="windowOrientation">
|
||||
<el-input v-model="form.windowOrientation" placeholder="例如:南" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="免租期" prop="rentFreePeriod">
|
||||
<el-input v-model="form.rentFreePeriod" placeholder="例如:前2个月" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="最短租期" prop="minLeaseTerm">
|
||||
<el-input v-model="form.minLeaseTerm" placeholder="例如:12个月" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="工位数量下限" prop="workstationMin">
|
||||
<el-input-number v-model="form.workstationMin" :min="0" :controls="false" placeholder="请输入最少工位数量" @change="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="工位数量上限" prop="workstationMax">
|
||||
<el-input-number v-model="form.workstationMax" :min="0" :controls="false" placeholder="请输入最多工位数量" @change="handleInputChange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item label="户型图说明" prop="floorPlanDesc">
|
||||
<el-input v-model="form.floorPlanDesc" type="textarea" :rows="3" placeholder="请输入户型图说明" @input="handleInputChange" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="户型图" prop="floorPlanImageUrls">
|
||||
<el-upload
|
||||
:action="uploadUrl"
|
||||
list-type="picture-card"
|
||||
:headers="uploadHeaders"
|
||||
:file-list="floorPlanImageList"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:on-success="handleFloorPlanImageSuccess"
|
||||
:on-remove="handleFloorPlanImageRemove"
|
||||
multiple>
|
||||
<i class="el-icon-plus"></i>
|
||||
</el-upload>
|
||||
<el-dialog :visible.sync="dialogVisible">
|
||||
<img width="100%" :src="dialogImageUrl" alt="">
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="form-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getRoom, addRoom, updateRoom, listProjects, listBuildings, listFloors, listRoomTags, listOwners } from '@/api/room'
|
||||
import { getProjectList } from '@/api/project'
|
||||
import { getBuildingList,getFloorList } from '@/api/building'
|
||||
export default {
|
||||
name: 'RoomForm',
|
||||
props: {
|
||||
isEdit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
roomId: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 当前激活的选项卡
|
||||
activeTab: 'basic',
|
||||
// 业主搜索加载中
|
||||
ownerLoading: false,
|
||||
// 对话框图片URL
|
||||
dialogImageUrl: '',
|
||||
// 图片预览对话框
|
||||
dialogVisible: false,
|
||||
// 项目选项
|
||||
projectOptions: [],
|
||||
// 楼宇选项
|
||||
buildingOptions: [],
|
||||
// 楼层选项
|
||||
floorOptions: [],
|
||||
// 标签选项
|
||||
tagOptions: [],
|
||||
// 业主选项
|
||||
ownerOptions: [],
|
||||
// 房源图片列表
|
||||
imageList: [],
|
||||
// 户型图列表
|
||||
floorPlanImageList: [],
|
||||
// 上传URL
|
||||
uploadUrl: process.env.VUE_APP_BASE_API + '/park/common/upload',
|
||||
// 上传请求头
|
||||
uploadHeaders: { },
|
||||
// 表单对象
|
||||
form: {
|
||||
projectId: undefined,
|
||||
buildingId: undefined,
|
||||
floorId: undefined,
|
||||
roomNumber: '',
|
||||
roomDigitalNumber: '',
|
||||
roomManageNumber: '',
|
||||
buildingArea: '',
|
||||
rentalArea: '',
|
||||
billingArea: '',
|
||||
innerArea: '',
|
||||
isVirtual: '0',
|
||||
deliveryTime: '',
|
||||
ownerId: undefined,
|
||||
propertyNature: '1',
|
||||
rentalStatus: '1', // 默认待租
|
||||
roomStatus: '1', // 默认公开
|
||||
roomType: '',
|
||||
roomRecordNumber: '',
|
||||
height: '',
|
||||
loadValue: '',
|
||||
tagIds: [],
|
||||
imageUrls: [],
|
||||
businessStatus: '1', // 默认招商
|
||||
availableDate: '',
|
||||
decorationStatus: '4', // 默认标准交付
|
||||
price: '',
|
||||
priceUnit: '2', // 默认元/㎡/月
|
||||
floorPrice: '',
|
||||
floorPriceUnit: '2', // 默认元/㎡/月
|
||||
businessNumber: '',
|
||||
businessCondition: '',
|
||||
rentFreePeriod: '',
|
||||
minLeaseTerm: '',
|
||||
usageRate: '',
|
||||
officeLayout: '',
|
||||
windowOrientation: '',
|
||||
workstationMin: null,
|
||||
workstationMax: null,
|
||||
floorPlanDesc: '',
|
||||
floorPlanImageUrls: []
|
||||
},
|
||||
// 表单校验规则
|
||||
rules: {
|
||||
projectId: [{ required: true, message: '请选择项目', trigger: 'change' }],
|
||||
buildingId: [{ required: true, message: '请选择楼宇', trigger: 'change' }],
|
||||
floorId: [{ required: true, message: '请选择楼层', trigger: 'change' }],
|
||||
roomNumber: [{ required: true, message: '请输入房号', trigger: 'blur' }],
|
||||
roomDigitalNumber: [{ required: true, message: '请输入房间数字编号', trigger: 'blur' }],
|
||||
rentalArea: [{ required: true, message: '请输入计租面积', trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getProjectOptions()
|
||||
this.getTagOptions()
|
||||
if (this.isEdit && this.roomId) {
|
||||
this.getRoomDetail()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听activeTab变化,切换tab时强制更新组件
|
||||
activeTab() {
|
||||
this.$nextTick(() => {
|
||||
// 强制更新表单,确保数据同步
|
||||
this.$forceUpdate()
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/** 获取房源详情 */
|
||||
getRoomDetail() {
|
||||
this.loading = true
|
||||
getRoom(this.roomId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.form = { ...response.data }
|
||||
|
||||
// 处理业务信息字段
|
||||
if (response.data.businessInfo) {
|
||||
this.form.rentalStatus = response.data.businessInfo.rentalStatus
|
||||
this.form.businessStatus = response.data.businessInfo.businessStatus
|
||||
this.form.availableDate = response.data.businessInfo.availableDate
|
||||
this.form.decorationStatus = response.data.businessInfo.decorationStatus
|
||||
this.form.price = response.data.businessInfo.price
|
||||
this.form.priceUnit = response.data.businessInfo.priceUnit
|
||||
this.form.floorPrice = response.data.businessInfo.floorPrice
|
||||
this.form.floorPriceUnit = response.data.businessInfo.floorPriceUnit
|
||||
this.form.businessNumber = response.data.businessInfo.businessNumber
|
||||
this.form.businessCondition = response.data.businessInfo.businessCondition
|
||||
}
|
||||
|
||||
// 处理扩展信息字段
|
||||
if (response.data.extendInfo) {
|
||||
this.form.roomRecordNumber = response.data.extendInfo.roomRecordNumber
|
||||
this.form.usageRate = response.data.extendInfo.usageRate
|
||||
this.form.officeLayout = response.data.extendInfo.officeLayout
|
||||
this.form.windowOrientation = response.data.extendInfo.windowOrientation
|
||||
this.form.rentFreePeriod = response.data.extendInfo.rentFreePeriod
|
||||
this.form.minLeaseTerm = response.data.extendInfo.minLeaseTerm
|
||||
this.form.workstationMin = response.data.extendInfo.workstationMin
|
||||
this.form.workstationMax = response.data.extendInfo.workstationMax
|
||||
this.form.floorPlanDesc = response.data.extendInfo.floorPlanDesc
|
||||
if (response.data.extendInfo.tags) {
|
||||
this.form.tagIds = response.data.extendInfo.tags.split(',')
|
||||
}
|
||||
}
|
||||
|
||||
// 处理图片数据
|
||||
this.form.imageUrls = []
|
||||
if (response.data.roomImages && Array.isArray(response.data.roomImages) && response.data.roomImages.length > 0) {
|
||||
this.form.imageUrls = response.data.roomImages.map(item => item.imageUrl)
|
||||
this.imageList = response.data.roomImages.map((item, index) => {
|
||||
return { name: '图片' + (index + 1), url: item.imageUrl }
|
||||
})
|
||||
}
|
||||
|
||||
// 处理户型图数据
|
||||
this.form.floorPlanImageUrls = []
|
||||
if (response.data.floorPlanImages && Array.isArray(response.data.floorPlanImages) && response.data.floorPlanImages.length > 0) {
|
||||
this.form.floorPlanImageUrls = response.data.floorPlanImages.map(item => item.imageUrl)
|
||||
this.floorPlanImageList = response.data.floorPlanImages.map((item, index) => {
|
||||
return { name: '户型图' + (index + 1), url: item.imageUrl }
|
||||
})
|
||||
}
|
||||
|
||||
// 加载关联数据
|
||||
this.getProjectOptions().then(() => {
|
||||
if (this.form.projectId) {
|
||||
this.getBuildingOptions(this.form.projectId).then(() => {
|
||||
if (this.form.buildingId) {
|
||||
this.getFloorOptions(this.form.buildingId)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 如果有业主ID,获取业主详情
|
||||
if (this.form.ownerId) {
|
||||
this.getOwnerDetail(this.form.ownerId)
|
||||
}
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取房源详情失败')
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取项目选项 */
|
||||
getProjectOptions() {
|
||||
getProjectList({ pageSize: 100 }).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.projectOptions = res.data.list.map(item => {
|
||||
return { id: item.id, projectName: item.projectName }
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取楼宇选项 */
|
||||
getBuildingOptions(projectId) {
|
||||
if(!projectId){
|
||||
this.buildingOptions = []
|
||||
this.form.buildingId = undefined
|
||||
this.floorOptions = []
|
||||
this.form.floorId = undefined
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
projectId: projectId,
|
||||
pageNum: 1,
|
||||
pageSize: 100,
|
||||
}
|
||||
getBuildingList(params).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.buildingOptions = response.data.list || []
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取楼宇列表失败')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取楼层选项 */
|
||||
getFloorOptions(buildingId) {
|
||||
if(!buildingId){
|
||||
this.floorOptions = []
|
||||
this.form.floorId = undefined
|
||||
return
|
||||
}
|
||||
return listFloors(buildingId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.floorOptions = response.data || []
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取楼层列表失败')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取房源标签选项 */
|
||||
getTagOptions() {
|
||||
listRoomTags().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.tagOptions = response.data
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取房源标签失败')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取业主详情 */
|
||||
getOwnerDetail(ownerId) {
|
||||
listOwners({ id: ownerId }).then(response => {
|
||||
if (response.code === '000000' && response.data && response.data.length > 0) {
|
||||
this.ownerOptions = response.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 远程搜索业主 */
|
||||
remoteSearchOwner(query) {
|
||||
if (query) {
|
||||
this.ownerLoading = true
|
||||
listOwners({ keyword: query }).then(response => {
|
||||
this.ownerLoading = false
|
||||
if (response.code === '000000') {
|
||||
this.ownerOptions = response.data
|
||||
} else {
|
||||
this.ownerOptions = []
|
||||
}
|
||||
}).catch(() => {
|
||||
this.ownerLoading = false
|
||||
})
|
||||
} else {
|
||||
this.ownerOptions = []
|
||||
}
|
||||
},
|
||||
|
||||
/** 项目选择改变事件 */
|
||||
handleProjectChange(projectId) {
|
||||
// 清空楼宇和楼层的选择
|
||||
this.form.buildingId = undefined
|
||||
this.form.floorId = undefined
|
||||
this.buildingOptions = []
|
||||
this.floorOptions = []
|
||||
|
||||
if (projectId) {
|
||||
this.getBuildingOptions(projectId)
|
||||
}
|
||||
},
|
||||
|
||||
/** 楼宇选择改变事件 */
|
||||
handleBuildingChange(buildingId) {
|
||||
// 清空楼层的选择
|
||||
this.form.floorId = undefined
|
||||
this.floorOptions = []
|
||||
|
||||
|
||||
if (buildingId) {
|
||||
getFloorList(buildingId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.floorOptions = response.data.list || []
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取楼层列表失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/** 图片预览 */
|
||||
handlePictureCardPreview(file) {
|
||||
this.dialogImageUrl = file.url
|
||||
this.dialogVisible = true
|
||||
},
|
||||
|
||||
/** 房源图片上传成功 */
|
||||
handleImageSuccess(response, file, fileList) {
|
||||
if (response.code === '000000') {
|
||||
this.form.imageUrls.push(response.data.imageUrl)
|
||||
this.imageList = fileList.map(file => {
|
||||
if (file.response) {
|
||||
return { name: file.name, url: file.response.data.imageUrl }
|
||||
} else {
|
||||
return { name: file.name, url: file.url }
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error('图片上传失败')
|
||||
}
|
||||
},
|
||||
|
||||
/** 房源图片删除 */
|
||||
handleImageRemove(file, fileList) {
|
||||
const url = file.url || (file.response && file.response.data.url)
|
||||
this.form.imageUrls = this.form.imageUrls.filter(item => item !== url)
|
||||
this.imageList = fileList
|
||||
},
|
||||
|
||||
/** 户型图上传成功 */
|
||||
handleFloorPlanImageSuccess(response, file, fileList) {
|
||||
if (response.code === '000000') {
|
||||
this.form.floorPlanImageUrls.push(response.data.imageUrl)
|
||||
this.floorPlanImageList = fileList.map(file => {
|
||||
if (file.response) {
|
||||
return { name: file.name, url: file.response.data.imageUrl }
|
||||
} else {
|
||||
return { name: file.name, url: file.url }
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$message.error('户型图上传失败')
|
||||
}
|
||||
},
|
||||
|
||||
/** 户型图删除 */
|
||||
handleFloorPlanImageRemove(file, fileList) {
|
||||
const url = file.url || (file.response && file.response.data.url)
|
||||
this.form.floorPlanImageUrls = this.form.floorPlanImageUrls.filter(item => item !== url)
|
||||
this.floorPlanImageList = fileList
|
||||
},
|
||||
|
||||
/** 提交表单 */
|
||||
submitForm() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (valid) {
|
||||
this.loading = true
|
||||
|
||||
// 准备提交数据
|
||||
const formData = {
|
||||
id: this.form.id,
|
||||
projectId: this.form.projectId,
|
||||
buildingId: this.form.buildingId,
|
||||
floorId: this.form.floorId,
|
||||
roomNumber: this.form.roomNumber,
|
||||
roomDigitalNumber: this.form.roomDigitalNumber,
|
||||
roomManageNumber: this.form.roomManageNumber,
|
||||
buildingArea: this.form.buildingArea,
|
||||
rentalArea: this.form.rentalArea,
|
||||
billingArea: this.form.billingArea,
|
||||
innerArea: this.form.innerArea,
|
||||
isVirtual: this.form.isVirtual,
|
||||
deliveryTime: this.formatDate(this.form.deliveryTime),
|
||||
ownerId: this.form.ownerId,
|
||||
propertyNature: this.form.propertyNature,
|
||||
height: this.form.height,
|
||||
loadValue: this.form.loadValue,
|
||||
roomStatus: this.form.roomStatus,
|
||||
roomType: this.form.roomType,
|
||||
remark: this.form.remark,
|
||||
|
||||
// 业务信息
|
||||
businessInfo: {
|
||||
rentalStatus: this.form.rentalStatus,
|
||||
businessStatus: this.form.businessStatus,
|
||||
availableDate: this.formatDate(this.form.availableDate),
|
||||
decorationStatus: this.form.decorationStatus,
|
||||
price: this.form.price,
|
||||
priceUnit: this.form.priceUnit,
|
||||
floorPrice: this.form.floorPrice,
|
||||
floorPriceUnit: this.form.floorPriceUnit,
|
||||
businessNumber: this.form.businessNumber,
|
||||
businessCondition: this.form.businessCondition
|
||||
},
|
||||
|
||||
// 扩展信息
|
||||
extendInfo: {
|
||||
roomRecordNumber: this.form.roomRecordNumber,
|
||||
usageRate: this.form.usageRate,
|
||||
officeLayout: this.form.officeLayout,
|
||||
windowOrientation: this.form.windowOrientation,
|
||||
rentFreePeriod: this.form.rentFreePeriod,
|
||||
minLeaseTerm: this.form.minLeaseTerm,
|
||||
workstationMin: this.form.workstationMin,
|
||||
workstationMax: this.form.workstationMax,
|
||||
floorPlanDesc: this.form.floorPlanDesc,
|
||||
tags: this.form.tagIds ? this.form.tagIds.join(',') : ''
|
||||
},
|
||||
|
||||
// 图片信息
|
||||
roomImages: this.form.imageUrls ? this.form.imageUrls.map((url, index) => {
|
||||
return {
|
||||
imageUrl: url,
|
||||
imageType: '房源图片',
|
||||
sortOrder: index
|
||||
}
|
||||
}) : [],
|
||||
|
||||
// 户型图信息
|
||||
floorPlanImages: this.form.floorPlanImageUrls ? this.form.floorPlanImageUrls.map((url, index) => {
|
||||
return {
|
||||
imageUrl: url,
|
||||
imageType: '户型图',
|
||||
sortOrder: index
|
||||
}
|
||||
}) : []
|
||||
}
|
||||
|
||||
const method = this.isEdit ? updateRoom : addRoom
|
||||
method(formData).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.$message.success(this.isEdit ? '修改成功' : '新增成功')
|
||||
this.$emit('refreshList')
|
||||
this.$emit('close')
|
||||
} else {
|
||||
this.$message.error(response.msg || (this.isEdit ? '修改失败' : '新增失败'))
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 取消按钮 */
|
||||
cancel() {
|
||||
this.$emit('close')
|
||||
},
|
||||
|
||||
/** 格式化日期为 YYYY-MM-DD */
|
||||
formatDate(date) {
|
||||
if (!date) return ''
|
||||
const d = new Date(date)
|
||||
const year = d.getFullYear()
|
||||
const month = (d.getMonth() + 1).toString().padStart(2, '0')
|
||||
const day = d.getDate().toString().padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
},
|
||||
|
||||
/** 处理输入变化 */
|
||||
handleInputChange() {
|
||||
this.$forceUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.room-form-container {
|
||||
padding: 10px;
|
||||
|
||||
.el-form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.form-footer {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
::v-deep .el-upload-list--picture-card .el-upload-list__item {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
::v-deep .el-upload--picture-card {
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
line-height: 120px;
|
||||
}
|
||||
|
||||
::v-deep .el-input-number {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
::v-deep .el-input-number .el-input__inner {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
653
pc/src/views/project/room/index.vue
Normal file
653
pc/src/views/project/room/index.vue
Normal file
@ -0,0 +1,653 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>房源列表</span>
|
||||
<el-button-group style="float: right;">
|
||||
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd">新增房源</el-button>
|
||||
<el-button type="primary" icon="el-icon-upload2" size="mini" @click="handleImport">批量导入</el-button>
|
||||
<el-button type="primary" icon="el-icon-download" size="mini" @click="handleExport">批量导出</el-button>
|
||||
</el-button-group>
|
||||
</div>
|
||||
|
||||
<!-- 标签页切换 -->
|
||||
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="全部房源" name="all"></el-tab-pane>
|
||||
<el-tab-pane label="招商房源" name="commercial"></el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 搜索条件 -->
|
||||
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="70px">
|
||||
<el-form-item label="项目" prop="projectId">
|
||||
<el-select v-model="queryParams.projectId" placeholder="请选择项目" clearable
|
||||
@change="handleProjectChange" style="width: 240px;">
|
||||
<el-option v-for="item in projectOptions" :key="item.id" :label="item.projectName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="楼宇" prop="buildingId">
|
||||
<el-select v-model="queryParams.buildingId" placeholder="请选择楼宇" clearable
|
||||
@change="handleBuildingChange" style="width: 240px;">
|
||||
<el-option v-for="item in buildingOptions" :key="item.id" :label="item.buildingName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="楼层" prop="floorId">
|
||||
<el-select v-model="queryParams.floorId" placeholder="请选择楼层" clearable style="width: 240px;">
|
||||
<el-option v-for="item in floorOptions" :key="item.id" :label="item.floorName" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="房号" prop="roomNumber">
|
||||
<el-input v-model="queryParams.roomNumber" placeholder="请输入房号" clearable style="width: 240px;" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标签" prop="tags">
|
||||
<el-select v-model="queryParams.tags" placeholder="请选择房源标签" clearable multiple style="width: 240px;">
|
||||
<el-option v-for="tag in tagOptions" :key="tag.id" :label="tag.tagName" :value="tag.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
||||
<el-button @click="toggleAdvanced" type="text">
|
||||
{{ advanced ? '收起' : '展开' }}
|
||||
<i :class="advanced ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 高级搜索条件,展开时显示 -->
|
||||
<el-form v-show="advanced" :model="queryParams" ref="advancedQueryForm" :inline="true" label-width="100px">
|
||||
<el-form-item label="租赁状态" prop="rentalStatus">
|
||||
<el-select v-model="queryParams.rentalStatus" placeholder="请选择租赁状态" clearable style="width: 240px;">
|
||||
<el-option label="待租" value="1" />
|
||||
<el-option label="已租" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="activeTab === 'all'" label="招商状态" prop="businessStatus">
|
||||
<el-select v-model="queryParams.businessStatus" placeholder="请选择招商状态" clearable style="width: 240px;">
|
||||
<el-option label="招商" value="1" />
|
||||
<el-option label="不招商" value="2" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="装修情况" prop="decorationStatus">
|
||||
<el-select v-model="queryParams.decorationStatus" placeholder="请选择装修情况" clearable style="width: 240px;">
|
||||
<el-option label="精装" value="1" />
|
||||
<el-option label="简装" value="2" />
|
||||
<el-option label="毛坯" value="3" />
|
||||
<el-option label="标准交付" value="4" />
|
||||
<el-option label="豪装" value="5" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="创建时间">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
style="width: 240px;"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 列表展示区域 -->
|
||||
<el-table v-loading="loading" :data="roomList" border style="width: 100%; margin-top: 15px;"
|
||||
@selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column label="项目名称" prop="projectName" min-width="140" show-overflow-tooltip />
|
||||
<el-table-column label="楼宇名称" prop="buildingName" min-width="120" show-overflow-tooltip />
|
||||
<el-table-column label="楼层" prop="floorName" min-width="80" />
|
||||
<el-table-column label="房号" prop="roomNumber" min-width="100" />
|
||||
<el-table-column label="计租面积(㎡)" prop="rentalArea" min-width="110" />
|
||||
<el-table-column label="报价" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.price }} {{ scope.row.priceUnit === 1 ? '元/㎡/天' : scope.row.priceUnit === 2 ? '元/㎡/月' : '元/月' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="租赁状态" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.rentalStatus === '1' ? 'success' : scope.row.rentalStatus === '2' ? 'danger' : 'warning'">
|
||||
{{ scope.row.rentalStatus === '1' ? '待租' : scope.row.rentalStatus === '2' ? '已租' : '暂无' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="装修情况" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.decorationStatus === '1' ? '精装' :
|
||||
scope.row.decorationStatus === '2' ? '简装' :
|
||||
scope.row.decorationStatus === '3' ? '毛坯' :
|
||||
scope.row.decorationStatus === '4' ? '标准交付' :
|
||||
scope.row.decorationStatus === '5' ? '豪装' : '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="交付时间" prop="deliveryTime" min-width="110" />
|
||||
<el-table-column label="可租日期" prop="availableDate" min-width="110" />
|
||||
<el-table-column label="房源标签" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-tag v-for="tag in scope.row.tagList" :key="tag.id" type="info" size="mini" style="margin-right: 4px">
|
||||
{{ tag.tagName }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="虚拟房源" min-width="90">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.isVirtual === '1' ? '是' : '否' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="招商状态" min-width="90">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.businessStatus === '1' ? 'primary' : 'info'">
|
||||
{{ scope.row.businessStatus === '1' ? '招商' : '不招商' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" prop="createTime" min-width="150" />
|
||||
<el-table-column label="操作" width="200" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="mini" @click="handleDetail(scope.row)">详情</el-button>
|
||||
<el-button type="text" size="mini" @click="handleUpdate(scope.row)">编辑</el-button>
|
||||
<el-button type="text" size="mini" @click="handleDelete(scope.row)" style="color: #F56C6C;">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="queryParams.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size="queryParams.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
class="pagination">
|
||||
</el-pagination>
|
||||
</el-card>
|
||||
|
||||
<!-- 导入对话框 -->
|
||||
<el-dialog :title="importTitle" :visible.sync="importOpen" width="500px" append-to-body>
|
||||
<el-upload
|
||||
ref="upload"
|
||||
:limit="1"
|
||||
accept=".xlsx, .xls"
|
||||
:headers="upload.headers"
|
||||
:action="upload.url"
|
||||
:disabled="upload.isUploading"
|
||||
:on-progress="handleFileUploadProgress"
|
||||
:on-success="handleFileSuccess"
|
||||
:auto-upload="false"
|
||||
drag>
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
<el-button type="text" @click="downloadImportTemplate">下载模板</el-button>
|
||||
<div>只允许导入xls、xlsx格式文件。</div>
|
||||
</div>
|
||||
</el-upload>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" @click="submitFileForm">确 定</el-button>
|
||||
<el-button @click="importOpen = false">取 消</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 查看详情对话框 -->
|
||||
<el-dialog :title="viewTitle" :visible.sync="viewOpen" width="900px" append-to-body>
|
||||
<detail-view v-if="viewOpen" :roomId="currentRoomId" />
|
||||
</el-dialog>
|
||||
|
||||
<!-- 添加/修改房源对话框 -->
|
||||
<el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
|
||||
<room-form
|
||||
v-if="open"
|
||||
ref="roomForm"
|
||||
:roomId="currentRoomId"
|
||||
:isEdit="isEdit"
|
||||
@refreshList="getList"
|
||||
@close="open = false" />
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listRoom, exportRoom, downloadTemplate, importRoom, listRoomTags, listProjects, listBuildings, listFloors, delRoom } from '@/api/room'
|
||||
import { getProjectList } from '@/api/project'
|
||||
import { getBuildingList,getFloorList } from '@/api/building'
|
||||
|
||||
import DetailView from './components/DetailView'
|
||||
import RoomForm from './components/RoomForm'
|
||||
|
||||
export default {
|
||||
name: 'ProjectRoom',
|
||||
components: {
|
||||
DetailView,
|
||||
RoomForm
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 遮罩层
|
||||
loading: false,
|
||||
// 选中数组
|
||||
ids: [],
|
||||
// 非单个禁用
|
||||
single: true,
|
||||
// 非多个禁用
|
||||
multiple: true,
|
||||
// 显示搜索条件
|
||||
advanced: false,
|
||||
// 总条数
|
||||
total: 0,
|
||||
// 房源表格数据
|
||||
roomList: [],
|
||||
// 弹出层标题
|
||||
title: '',
|
||||
// 是否显示弹出层
|
||||
open: false,
|
||||
// 是否显示详情弹出层
|
||||
viewOpen: false,
|
||||
// 是否显示导入弹出层
|
||||
importOpen: false,
|
||||
// 导入标题
|
||||
importTitle: '导入房源数据',
|
||||
// 详情标题
|
||||
viewTitle: '房源详情',
|
||||
// 日期范围
|
||||
dateRange: [],
|
||||
// 当前处理的房源ID
|
||||
currentRoomId: null,
|
||||
// 是否编辑模式
|
||||
isEdit: false,
|
||||
// 当前激活的Tab页
|
||||
activeTab: 'all',
|
||||
// 查询参数
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectId: undefined,
|
||||
buildingId: undefined,
|
||||
floorId: undefined,
|
||||
roomNumber: undefined,
|
||||
tags: [],
|
||||
rentalStatus: undefined,
|
||||
businessStatus: undefined,
|
||||
decorationStatus: undefined
|
||||
},
|
||||
// 项目选项
|
||||
projectOptions: [],
|
||||
// 楼宇选项
|
||||
buildingOptions: [],
|
||||
// 楼层选项
|
||||
floorOptions: [],
|
||||
// 标签选项
|
||||
tagOptions: [],
|
||||
// 上传参数
|
||||
upload: {
|
||||
// 是否禁用上传
|
||||
isUploading: false,
|
||||
// 设置上传的请求头部
|
||||
headers: { },
|
||||
// 上传的URL
|
||||
url: process.env.VUE_APP_BASE_API + '/room/import'
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.getProjectOptions()
|
||||
this.getTagOptions()
|
||||
},
|
||||
methods: {
|
||||
/** 处理Tab切换 */
|
||||
handleTabClick() {
|
||||
// 重置页码
|
||||
this.queryParams.pageNum = 1
|
||||
|
||||
if (this.activeTab === 'all') {
|
||||
// 全部房源,清除businessStatus的查询条件
|
||||
this.queryParams.businessStatus = undefined
|
||||
} else if (this.activeTab === 'commercial') {
|
||||
// 招商房源,设置businessStatus为1
|
||||
this.queryParams.businessStatus = '1'
|
||||
}
|
||||
|
||||
// 重新加载列表
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 获取房源列表 */
|
||||
getList() {
|
||||
this.loading = true
|
||||
|
||||
// 添加日期范围参数
|
||||
const params = {...this.queryParams}
|
||||
if (this.dateRange && this.dateRange.length > 0) {
|
||||
params.beginTime = this.dateRange[0]
|
||||
params.endTime = this.dateRange[1]
|
||||
}
|
||||
|
||||
listRoom(params).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.roomList = response.data.list || []
|
||||
this.total = response.data.total
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取房源列表失败')
|
||||
this.roomList = []
|
||||
this.total = 0
|
||||
}
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取项目选项 */
|
||||
getProjectOptions() {
|
||||
// 调用项目列表接口获取项目选项
|
||||
getProjectList({ pageSize: 100 }).then(res => {
|
||||
if (res.code === '000000') {
|
||||
this.projectOptions = res.data.list.map(item => {
|
||||
return { id: item.id, projectName: item.projectName }
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取楼宇选项 */
|
||||
getBuildingOptions(projectId) {
|
||||
if (!projectId) {
|
||||
this.buildingOptions = []
|
||||
this.queryParams.buildingId = undefined
|
||||
this.floorOptions = []
|
||||
this.queryParams.floorId = undefined
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
projectId: projectId,
|
||||
pageNum: 1,
|
||||
pageSize: 100,
|
||||
}
|
||||
getBuildingList(params).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.buildingOptions = response.data.list || []
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取楼宇列表失败')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取楼层选项 */
|
||||
getFloorOptions(buildingId) {
|
||||
if (!buildingId) {
|
||||
this.floorOptions = []
|
||||
this.queryParams.floorId = undefined
|
||||
return
|
||||
}
|
||||
|
||||
listFloors(buildingId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.floorOptions = response.data || []
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取楼层列表失败')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取房源标签选项 */
|
||||
getTagOptions() {
|
||||
listRoomTags().then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.tagOptions = response.data || []
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取房源标签失败')
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
/** 项目选择改变事件 */
|
||||
handleProjectChange(projectId) {
|
||||
// 清空楼宇和楼层的选择
|
||||
this.queryParams.buildingId = undefined
|
||||
this.queryParams.floorId = undefined
|
||||
this.buildingOptions = []
|
||||
this.floorOptions = []
|
||||
|
||||
if (projectId) {
|
||||
this.getBuildingOptions(projectId)
|
||||
}
|
||||
},
|
||||
|
||||
/** 楼宇选择改变事件 */
|
||||
handleBuildingChange(buildingId) {
|
||||
// 清空楼层的选择
|
||||
this.queryParams.floorId = undefined
|
||||
this.floorOptions = []
|
||||
|
||||
if (buildingId) {
|
||||
getFloorList(buildingId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.floorOptions = response.data.list || []
|
||||
} else {
|
||||
this.$message.error(response.msg || '获取楼层列表失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/** 查询房源列表 */
|
||||
handleQuery() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 重置查询条件 */
|
||||
resetQuery() {
|
||||
this.dateRange = []
|
||||
this.resetForm('queryForm')
|
||||
this.resetForm('advancedQueryForm')
|
||||
|
||||
// 保持当前Tab的状态
|
||||
const tabBusinessStatus = this.activeTab === 'commercial' ? '1' : undefined
|
||||
|
||||
this.queryParams = {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
projectId: undefined,
|
||||
buildingId: undefined,
|
||||
floorId: undefined,
|
||||
roomNumber: undefined,
|
||||
tags: [],
|
||||
rentalStatus: undefined,
|
||||
businessStatus: tabBusinessStatus,
|
||||
decorationStatus: undefined
|
||||
}
|
||||
|
||||
this.buildingOptions = []
|
||||
this.floorOptions = []
|
||||
this.handleQuery()
|
||||
},
|
||||
|
||||
/** 切换高级搜索 */
|
||||
toggleAdvanced() {
|
||||
this.advanced = !this.advanced
|
||||
},
|
||||
|
||||
/** 多选框选中数据 */
|
||||
handleSelectionChange(selection) {
|
||||
this.ids = selection.map(item => item.id)
|
||||
this.single = selection.length !== 1
|
||||
this.multiple = !selection.length
|
||||
},
|
||||
|
||||
/** 导出房源数据 */
|
||||
handleExport() {
|
||||
const params = this.ids
|
||||
if(this.ids.length === 0){
|
||||
this.$message.warning('请至少选择一条数据')
|
||||
return
|
||||
}
|
||||
|
||||
this.$confirm('是否确认导出所选房源数据?', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.loading = true
|
||||
exportRoom(params).then(response => {
|
||||
this.download(response, '房源信息.xlsx')
|
||||
this.loading = false
|
||||
}).catch(() => {
|
||||
this.loading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/** 下载导入模板 */
|
||||
downloadImportTemplate() {
|
||||
downloadTemplate().then(response => {
|
||||
this.download(response, 'room_import.xlsx')
|
||||
})
|
||||
},
|
||||
|
||||
/** 导入按钮操作 */
|
||||
handleImport() {
|
||||
this.importOpen = true
|
||||
},
|
||||
|
||||
/** 文件上传中处理 */
|
||||
handleFileUploadProgress() {
|
||||
this.upload.isUploading = true
|
||||
},
|
||||
|
||||
/** 文件上传成功处理 */
|
||||
handleFileSuccess(response) {
|
||||
this.upload.isUploading = false
|
||||
this.$refs.upload.clearFiles()
|
||||
this.importOpen = false
|
||||
if (response.code === '000000') {
|
||||
this.$alert('导入成功' + (response.data ? response.data.successCount || 0 : 0) + '条数据', '导入结果', { type: 'success' })
|
||||
this.getList()
|
||||
} else {
|
||||
this.$alert(response.msg || '导入失败', '导入结果', { type: 'error' })
|
||||
}
|
||||
},
|
||||
|
||||
/** 提交上传文件 */
|
||||
submitFileForm() {
|
||||
this.$refs.upload.submit()
|
||||
},
|
||||
|
||||
/** 新增按钮操作 */
|
||||
handleAdd() {
|
||||
this.currentRoomId = null
|
||||
this.isEdit = false
|
||||
this.title = '新增房源'
|
||||
this.open = true
|
||||
},
|
||||
|
||||
/** 修改按钮操作 */
|
||||
handleUpdate(row) {
|
||||
this.currentRoomId = row.id
|
||||
this.isEdit = true
|
||||
this.title = '修改房源'
|
||||
this.open = true
|
||||
},
|
||||
|
||||
/** 查看详情按钮操作 */
|
||||
handleDetail(row) {
|
||||
this.currentRoomId = row.id
|
||||
this.viewTitle = '房源详情'
|
||||
this.viewOpen = true
|
||||
},
|
||||
|
||||
/** 删除按钮操作 */
|
||||
handleDelete(row) {
|
||||
const roomId = row.id
|
||||
this.$confirm('是否确认删除该房源?删除后所有相关信息将被清除', '警告', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delRoom(roomId).then(response => {
|
||||
if (response.code === '000000') {
|
||||
this.$message.success('删除成功')
|
||||
this.getList()
|
||||
} else {
|
||||
this.$message.error(response.msg || '删除失败')
|
||||
}
|
||||
})
|
||||
}).catch(() => {})
|
||||
},
|
||||
|
||||
/** 分页大小改变 */
|
||||
handleSizeChange(val) {
|
||||
this.queryParams.pageSize = val
|
||||
this.getList()
|
||||
},
|
||||
|
||||
/** 分页页码改变 */
|
||||
handleCurrentChange(val) {
|
||||
this.queryParams.pageNum = val
|
||||
this.getList()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
.el-card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 15px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.el-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
::v-deep .el-upload-dragger {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
::v-deep .el-upload {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
display: block;
|
||||
}
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
::v-deep .el-input--suffix .el-input__inner {
|
||||
padding-right: 30px;
|
||||
}
|
||||
|
||||
/* 确保表格内容垂直居中 */
|
||||
::v-deep .el-table td, ::v-deep .el-table th {
|
||||
padding: 8px 0;
|
||||
}
|
||||
}
|
||||
::v-deep .el-range-separator {
|
||||
width: 30px !important; /* 增加分隔符宽度 */
|
||||
padding: 0 5px !important;
|
||||
line-height: 25px !important;
|
||||
}
|
||||
::v-deep .el-date-editor .el-range__icon {
|
||||
line-height: 25px !important;
|
||||
}
|
||||
</style>
|
Loading…
x
Reference in New Issue
Block a user