feat
This commit is contained in:
@@ -1,46 +1,92 @@
|
|||||||
import { request } from "@/api/request";
|
import { request } from '@/api/request'
|
||||||
|
|
||||||
|
/** 资产分类数据类型 */
|
||||||
|
export interface AssetCategory {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
description?: string
|
||||||
|
icon?: string
|
||||||
|
color?: string
|
||||||
|
parent_id?: number
|
||||||
|
parent?: AssetCategory
|
||||||
|
level: number
|
||||||
|
path?: string
|
||||||
|
enabled: boolean
|
||||||
|
sort: number
|
||||||
|
asset_count: number
|
||||||
|
remarks?: string
|
||||||
|
created_by?: string
|
||||||
|
updated_by?: string
|
||||||
|
created_at?: string
|
||||||
|
updated_at?: string
|
||||||
|
children?: AssetCategory[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分页列表参数 */
|
||||||
|
export interface CategoryListParams {
|
||||||
|
page?: number
|
||||||
|
page_size?: number
|
||||||
|
sort?: string
|
||||||
|
order?: 'asc' | 'desc'
|
||||||
|
keyword?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建/更新分类参数 */
|
||||||
|
export interface CategoryFormData {
|
||||||
|
id?: number
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
description?: string
|
||||||
|
icon?: string
|
||||||
|
color?: string
|
||||||
|
parent_id?: number | null
|
||||||
|
enabled?: boolean
|
||||||
|
sort?: number
|
||||||
|
remarks?: string
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取资产分类列表(分页) */
|
/** 获取资产分类列表(分页) */
|
||||||
export const fetchCategoryList = (data?: {
|
export const fetchCategoryList = (params?: CategoryListParams) => {
|
||||||
page?: number;
|
return request.post('/Assets/v1/category/list', params || {})
|
||||||
page_size?: number;
|
}
|
||||||
keyword?: string;
|
|
||||||
parent_id?: number;
|
|
||||||
}) => {
|
|
||||||
return request.post("/Assets/v1/category/list", data || {});
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 获取资产分类详情 */
|
/** 获取资产分类详情 */
|
||||||
export const fetchCategoryDetail = (id: number) => {
|
export const fetchCategoryDetail = (id: number) => {
|
||||||
return request.get(`/Assets/v1/category/detail/${id}`);
|
return request.get(`/Assets/v1/category/detail/${id}`)
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 创建资产分类 */
|
/** 创建资产分类 */
|
||||||
export const createCategory = (data: any) => {
|
export const createCategory = (data: CategoryFormData) => {
|
||||||
return request.post("/Assets/v1/category/create", data);
|
return request.post('/Assets/v1/category/create', data)
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 更新资产分类 */
|
/** 更新资产分类 */
|
||||||
export const updateCategory = (data: any) => {
|
export const updateCategory = (data: CategoryFormData) => {
|
||||||
return request.put("/Assets/v1/category/update", data);
|
return request.put('/Assets/v1/category/update', data)
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 删除资产分类 */
|
/** 删除资产分类 */
|
||||||
export const deleteCategory = (id: number) => {
|
export const deleteCategory = (id: number) => {
|
||||||
return request.delete(`/Assets/v1/category/delete/${id}`);
|
return request.delete(`/Assets/v1/category/delete/${id}`)
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 获取所有资产分类(用于下拉选择) */
|
/** 获取所有资产分类(用于下拉选择) */
|
||||||
export const fetchAllCategories = () => {
|
export const fetchAllCategories = (keyword?: string) => {
|
||||||
return request.get("/Assets/v1/category/all");
|
return request.get('/Assets/v1/category/all', { params: { keyword } })
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 获取资产分类树形结构 */
|
/** 获取资产分类树形结构 */
|
||||||
export const fetchCategoryTree = () => {
|
export const fetchCategoryTree = (keyword?: string) => {
|
||||||
return request.get("/Assets/v1/category/tree");
|
return request.get('/Assets/v1/category/tree', { params: { keyword } })
|
||||||
};
|
}
|
||||||
|
|
||||||
/** 获取指定分类的子分类列表 */
|
/** 获取指定分类的子分类列表 */
|
||||||
export const fetchCategoryChildren = (id: number) => {
|
export const fetchCategoryChildren = (id: number) => {
|
||||||
return request.get(`/Assets/v1/category/children/${id}`);
|
return request.get(`/Assets/v1/category/children/${id}`)
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/** 根据父级分类ID获取分类列表 */
|
||||||
|
export const fetchCategoryByParent = (parentId: number) => {
|
||||||
|
return request.get(`/Assets/v1/category/parent/${parentId}`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ export const deleteSupplier = (id: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** 获取所有供应商(用于下拉选择) */
|
/** 获取所有供应商(用于下拉选择) */
|
||||||
export const fetchAllSuppliers = (keyword?: string, status?: string) => {
|
export const fetchAllSuppliers = (keyword?: string, status?: string, pageSize?: number) => {
|
||||||
const params: any = {};
|
const params: any = {};
|
||||||
if (keyword) params.keyword = keyword;
|
if (keyword) params.keyword = keyword;
|
||||||
if (status) params.status = status;
|
if (status) params.status = status;
|
||||||
|
if (pageSize) params.page_size = pageSize;
|
||||||
return request.get("/Assets/v1/supplier/all", { params });
|
return request.get("/Assets/v1/supplier/all", { params });
|
||||||
};
|
};
|
||||||
@@ -96,7 +96,7 @@ const OPS: AppRouteRecordRaw = {
|
|||||||
// {
|
// {
|
||||||
// path: 'assets/device',
|
// path: 'assets/device',
|
||||||
// name: 'AssetsDevice',
|
// name: 'AssetsDevice',
|
||||||
// component: () => import('@/views/ops/pages/assets/device/index.vue'),
|
// component: () => import('@/views/ops/pages/assets/device/list/index.vue'),
|
||||||
// meta: {
|
// meta: {
|
||||||
// locale: '设备管理',
|
// locale: '设备管理',
|
||||||
// requiresAuth: true,
|
// requiresAuth: true,
|
||||||
@@ -104,9 +104,20 @@ const OPS: AppRouteRecordRaw = {
|
|||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
// path: 'assets/device/form',
|
||||||
|
// name: 'AssetsDeviceForm',
|
||||||
|
// component: () => import('@/views/ops/pages/assets/device/form/index.vue'),
|
||||||
|
// meta: {
|
||||||
|
// locale: '新增/编辑设备',
|
||||||
|
// requiresAuth: true,
|
||||||
|
// roles: ['*'],
|
||||||
|
// hideInMenu: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
// path: 'assets/supplier',
|
// path: 'assets/supplier',
|
||||||
// name: 'AssetsSupplier',
|
// name: 'AssetsSupplier',
|
||||||
// component: () => import('@/views/ops/pages/assets/supplier/index.vue'),
|
// component: () => import('@/views/ops/pages/assets/supplier/list/index.vue'),
|
||||||
// meta: {
|
// meta: {
|
||||||
// locale: '供应商管理',
|
// locale: '供应商管理',
|
||||||
// requiresAuth: true,
|
// requiresAuth: true,
|
||||||
@@ -114,6 +125,17 @@ const OPS: AppRouteRecordRaw = {
|
|||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// {
|
// {
|
||||||
|
// path: 'assets/supplier/form',
|
||||||
|
// name: 'AssetsSupplierForm',
|
||||||
|
// component: () => import('@/views/ops/pages/assets/supplier/form/index.vue'),
|
||||||
|
// meta: {
|
||||||
|
// locale: '新增/编辑供应商',
|
||||||
|
// requiresAuth: true,
|
||||||
|
// roles: ['*'],
|
||||||
|
// hideInMenu: true,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
// path: 'assets/classify',
|
// path: 'assets/classify',
|
||||||
// name: 'AssetsClassify',
|
// name: 'AssetsClassify',
|
||||||
// component: () => import('@/views/ops/pages/assets/classify/index.vue'),
|
// component: () => import('@/views/ops/pages/assets/classify/index.vue'),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,6 @@
|
|||||||
<a-checkbox checked="rememberPassword" :model-value="loginConfig.rememberPassword" @change="setRememberPassword as any">
|
<a-checkbox checked="rememberPassword" :model-value="loginConfig.rememberPassword" @change="setRememberPassword as any">
|
||||||
{{ $t('login.form.rememberPassword') }}
|
{{ $t('login.form.rememberPassword') }}
|
||||||
</a-checkbox>
|
</a-checkbox>
|
||||||
<a-link>{{ $t('login.form.forgetPassword') }}</a-link>
|
|
||||||
</div>
|
</div>
|
||||||
<a-button type="primary" html-type="submit" long :loading="loading">
|
<a-button type="primary" html-type="submit" long :loading="loading">
|
||||||
{{ $t('login.form.login') }}
|
{{ $t('login.form.login') }}
|
||||||
|
|||||||
@@ -1,165 +1,181 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
:visible="visible"
|
v-model:visible="dialogVisible"
|
||||||
title="分类详情"
|
title="分类详情"
|
||||||
width="600px"
|
:width="600"
|
||||||
@cancel="handleCancel"
|
|
||||||
@update:visible="handleVisibleChange"
|
|
||||||
:footer="false"
|
:footer="false"
|
||||||
|
unmount-on-close
|
||||||
>
|
>
|
||||||
<a-descriptions :data="detailData" layout="vertical" bordered :column="2" v-if="category">
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
<a-descriptions-item label="分类名称" :span="1">
|
<a-descriptions
|
||||||
{{ category.name }}
|
v-if="categoryDetail"
|
||||||
</a-descriptions-item>
|
:column="2"
|
||||||
<a-descriptions-item label="分类编码" :span="1">
|
bordered
|
||||||
{{ category.code }}
|
size="medium"
|
||||||
</a-descriptions-item>
|
>
|
||||||
<a-descriptions-item label="父级分类" :span="1">
|
<a-descriptions-item label="分类名称">
|
||||||
{{ category.parent?.name || '无(一级分类)' }}
|
{{ categoryDetail.name }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="层级" :span="1">
|
<a-descriptions-item label="分类编码">
|
||||||
{{ category.level }}
|
{{ categoryDetail.code }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="排序" :span="1">
|
<a-descriptions-item label="层级">
|
||||||
{{ category.sort }}
|
<a-tag :color="getLevelColor(categoryDetail.level)">
|
||||||
</a-descriptions-item>
|
第{{ categoryDetail.level }}级
|
||||||
<a-descriptions-item label="是否启用" :span="1">
|
</a-tag>
|
||||||
<a-tag :color="category.enabled ? 'green' : 'red'">
|
</a-descriptions-item>
|
||||||
{{ category.enabled ? '是' : '否' }}
|
<a-descriptions-item label="父级分类">
|
||||||
</a-tag>
|
{{ categoryDetail.parent?.name || '-' }}
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="颜色标识" :span="2">
|
<a-descriptions-item label="资产数量">
|
||||||
<div class="color-display">
|
<a-badge :count="categoryDetail.asset_count" :max-count="999" />
|
||||||
<div
|
</a-descriptions-item>
|
||||||
class="color-block"
|
<a-descriptions-item label="排序">
|
||||||
:style="{ backgroundColor: category.color || '#ccc' }"
|
{{ categoryDetail.sort }}
|
||||||
></div>
|
</a-descriptions-item>
|
||||||
<span>{{ category.color || '-' }}</span>
|
<a-descriptions-item label="状态">
|
||||||
</div>
|
<a-tag :color="categoryDetail.enabled ? 'green' : 'red'">
|
||||||
</a-descriptions-item>
|
{{ categoryDetail.enabled ? '启用' : '禁用' }}
|
||||||
<a-descriptions-item label="图标路径" :span="2">
|
</a-tag>
|
||||||
{{ category.icon || '-' }}
|
</a-descriptions-item>
|
||||||
</a-descriptions-item>
|
<a-descriptions-item label="颜色">
|
||||||
<a-descriptions-item label="描述" :span="2">
|
<div v-if="categoryDetail.color" style="display: flex; align-items: center; gap: 8px">
|
||||||
{{ category.description || '-' }}
|
<div
|
||||||
</a-descriptions-item>
|
:style="{
|
||||||
<a-descriptions-item label="备注" :span="2">
|
width: '20px',
|
||||||
{{ category.remarks || '-' }}
|
height: '20px',
|
||||||
</a-descriptions-item>
|
borderRadius: '4px',
|
||||||
<a-descriptions-item label="创建时间" :span="1">
|
backgroundColor: categoryDetail.color,
|
||||||
{{ formatDate(category.created_at) }}
|
}"
|
||||||
</a-descriptions-item>
|
/>
|
||||||
<a-descriptions-item label="更新时间" :span="1">
|
<span>{{ categoryDetail.color }}</span>
|
||||||
{{ formatDate(category.updated_at) }}
|
</div>
|
||||||
</a-descriptions-item>
|
<span v-else>-</span>
|
||||||
<a-descriptions-item label="创建人" :span="1">
|
</a-descriptions-item>
|
||||||
{{ category.created_by || '-' }}
|
<a-descriptions-item label="分类路径">
|
||||||
</a-descriptions-item>
|
{{ categoryDetail.path || '-' }}
|
||||||
<a-descriptions-item label="更新人" :span="1">
|
</a-descriptions-item>
|
||||||
{{ category.updated_by || '-' }}
|
<a-descriptions-item label="描述" :span="2">
|
||||||
</a-descriptions-item>
|
{{ categoryDetail.description || '-' }}
|
||||||
</a-descriptions>
|
</a-descriptions-item>
|
||||||
<a-empty v-else description="暂无数据" />
|
<a-descriptions-item label="备注" :span="2">
|
||||||
|
{{ categoryDetail.remarks || '-' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="创建人">
|
||||||
|
{{ categoryDetail.created_by || '-' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="更新人">
|
||||||
|
{{ categoryDetail.updated_by || '-' }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="创建时间">
|
||||||
|
{{ formatDateTime(categoryDetail.created_at) }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="更新时间">
|
||||||
|
{{ formatDateTime(categoryDetail.updated_at) }}
|
||||||
|
</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
|
||||||
|
<a-empty v-else-if="!loading" description="暂无数据" />
|
||||||
|
</a-spin>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
interface Category {
|
import { fetchCategoryDetail } from '@/api/ops/assetCategory'
|
||||||
id?: number
|
import type { AssetCategory } from '@/api/ops/assetCategory'
|
||||||
name?: string
|
|
||||||
code?: string
|
|
||||||
description?: string
|
|
||||||
icon?: string
|
|
||||||
color?: string
|
|
||||||
parent_id?: number
|
|
||||||
parent?: any
|
|
||||||
level?: number
|
|
||||||
sort?: number
|
|
||||||
enabled?: boolean
|
|
||||||
remarks?: string
|
|
||||||
created_at?: string
|
|
||||||
updated_at?: string
|
|
||||||
created_by?: string
|
|
||||||
updated_by?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible: boolean
|
visible: boolean
|
||||||
category: Category | null
|
categoryId?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'update:visible', value: boolean): void
|
(e: 'update:visible', visible: boolean): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
const emit = defineEmits<Emits>()
|
visible: false,
|
||||||
|
categoryId: undefined,
|
||||||
// 详情数据
|
|
||||||
const detailData = computed(() => {
|
|
||||||
if (!props.category) return []
|
|
||||||
return [
|
|
||||||
{ label: '分类名称', value: props.category.name },
|
|
||||||
{ label: '分类编码', value: props.category.code },
|
|
||||||
{ label: '父级分类', value: props.category.parent?.name || '无(一级分类)' },
|
|
||||||
{ label: '层级', value: props.category.level },
|
|
||||||
{ label: '排序', value: props.category.sort },
|
|
||||||
{ label: '是否启用', value: props.category.enabled ? '是' : '否' },
|
|
||||||
{ label: '颜色标识', value: props.category.color || '-' },
|
|
||||||
{ label: '图标路径', value: props.category.icon || '-' },
|
|
||||||
{ label: '描述', value: props.category.description || '-' },
|
|
||||||
{ label: '备注', value: props.category.remarks || '-' },
|
|
||||||
{ label: '创建时间', value: formatDate(props.category.created_at) },
|
|
||||||
{ label: '更新时间', value: formatDate(props.category.updated_at) },
|
|
||||||
{ label: '创建人', value: props.category.created_by || '-' },
|
|
||||||
{ label: '更新人', value: props.category.updated_by || '-' },
|
|
||||||
]
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 格式化日期
|
const emit = defineEmits<Emits>()
|
||||||
const formatDate = (dateStr?: string) => {
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const categoryDetail = ref<AssetCategory | null>(null)
|
||||||
|
|
||||||
|
const dialogVisible = computed({
|
||||||
|
get: () => props.visible,
|
||||||
|
set: (val) => emit('update:visible', val),
|
||||||
|
})
|
||||||
|
|
||||||
|
// 获取层级颜色
|
||||||
|
const getLevelColor = (level: number) => {
|
||||||
|
const colors: Record<number, string> = {
|
||||||
|
1: 'blue',
|
||||||
|
2: 'green',
|
||||||
|
3: 'orange',
|
||||||
|
4: 'purple',
|
||||||
|
5: 'cyan',
|
||||||
|
}
|
||||||
|
return colors[level] || 'gray'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期时间
|
||||||
|
const formatDateTime = (dateStr?: string) => {
|
||||||
if (!dateStr) return '-'
|
if (!dateStr) return '-'
|
||||||
const date = new Date(dateStr)
|
try {
|
||||||
return date.toLocaleString('zh-CN', {
|
const date = new Date(dateStr)
|
||||||
year: 'numeric',
|
return date.toLocaleString('zh-CN', {
|
||||||
month: '2-digit',
|
year: 'numeric',
|
||||||
day: '2-digit',
|
month: '2-digit',
|
||||||
hour: '2-digit',
|
day: '2-digit',
|
||||||
minute: '2-digit',
|
hour: '2-digit',
|
||||||
second: '2-digit',
|
minute: '2-digit',
|
||||||
})
|
})
|
||||||
|
} catch {
|
||||||
|
return dateStr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消
|
// 加载分类详情
|
||||||
const handleCancel = () => {
|
const loadCategoryDetail = async () => {
|
||||||
emit('update:visible', false)
|
if (!props.categoryId) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await fetchCategoryDetail(props.categoryId)
|
||||||
|
if (res.code === 0) {
|
||||||
|
categoryDetail.value = res.details
|
||||||
|
} else {
|
||||||
|
Message.error(res.message || '获取分类详情失败')
|
||||||
|
categoryDetail.value = null
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取分类详情失败:', error)
|
||||||
|
Message.error('获取分类详情失败')
|
||||||
|
categoryDetail.value = null
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理对话框可见性变化
|
// 监听 visible 变化
|
||||||
const handleVisibleChange = (visible: boolean) => {
|
watch(
|
||||||
emit('update:visible', visible)
|
() => props.visible,
|
||||||
}
|
(val) => {
|
||||||
|
if (val && props.categoryId) {
|
||||||
|
loadCategoryDetail()
|
||||||
|
} else {
|
||||||
|
categoryDetail.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'CategoryDetailDialog',
|
name: 'CategoryDetailDialog',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.color-display {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
|
|
||||||
.color-block {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,137 +1,96 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
:visible="visible"
|
v-model:visible="dialogVisible"
|
||||||
:title="isEdit ? '编辑分类' : '新增分类'"
|
:title="isEdit ? '编辑分类' : '新建分类'"
|
||||||
width="600px"
|
:width="520"
|
||||||
@ok="handleOk"
|
:mask-closable="false"
|
||||||
|
unmount-on-close
|
||||||
@cancel="handleCancel"
|
@cancel="handleCancel"
|
||||||
@update:visible="handleVisibleChange"
|
@ok="handleOk"
|
||||||
:confirm-loading="submitting"
|
|
||||||
>
|
>
|
||||||
<a-form :model="form" layout="vertical" ref="formRef">
|
<a-form ref="formRef" :model="formData" :rules="rules" layout="vertical">
|
||||||
<a-row :gutter="16">
|
<a-form-item field="name" label="分类名称" required>
|
||||||
<a-col :span="12">
|
<a-input
|
||||||
<a-form-item
|
v-model="formData.name"
|
||||||
label="分类名称"
|
placeholder="请输入分类名称"
|
||||||
field="name"
|
:max-length="50"
|
||||||
:rules="[{ required: true, message: '请输入分类名称' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.name"
|
|
||||||
placeholder="请输入分类名称"
|
|
||||||
:max-length="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="分类编码"
|
|
||||||
field="code"
|
|
||||||
:rules="[{ required: true, message: '请输入分类编码' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.code"
|
|
||||||
placeholder="请输入分类编码"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-form-item
|
|
||||||
v-if="showParentSelector !== false"
|
|
||||||
label="父级分类"
|
|
||||||
field="parent_id"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model="form.parent_id"
|
|
||||||
placeholder="请选择父级分类(不选则为一级分类)"
|
|
||||||
allow-clear
|
|
||||||
allow-search
|
|
||||||
:disabled="fixedParentId !== undefined"
|
|
||||||
:filter-option="filterParentOption"
|
|
||||||
>
|
|
||||||
<a-option
|
|
||||||
v-for="item in parentCategories"
|
|
||||||
:key="item.id"
|
|
||||||
:value="item.id"
|
|
||||||
>
|
|
||||||
{{ item.name }}
|
|
||||||
</a-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="排序" field="sort">
|
|
||||||
<a-input-number
|
|
||||||
v-model="form.sort"
|
|
||||||
placeholder="请输入排序"
|
|
||||||
:min="0"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="是否启用" field="enabled">
|
|
||||||
<a-switch v-model="form.enabled" />
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-form-item label="颜色标识" field="color">
|
|
||||||
<div class="color-picker-wrapper">
|
|
||||||
<a-input
|
|
||||||
v-model="form.color"
|
|
||||||
placeholder="请选择颜色标识"
|
|
||||||
readonly
|
|
||||||
@click="showColorPicker = !showColorPicker"
|
|
||||||
>
|
|
||||||
<template #prefix>
|
|
||||||
<div
|
|
||||||
class="color-preview"
|
|
||||||
:style="{ backgroundColor: form.color || '#ccc' }"
|
|
||||||
></div>
|
|
||||||
</template>
|
|
||||||
</a-input>
|
|
||||||
<div v-if="showColorPicker" class="color-picker-dropdown">
|
|
||||||
<div class="color-picker-header">选择颜色</div>
|
|
||||||
<div class="color-picker-grid">
|
|
||||||
<div
|
|
||||||
v-for="color in presetColors"
|
|
||||||
:key="color"
|
|
||||||
class="color-item"
|
|
||||||
:style="{ backgroundColor: color }"
|
|
||||||
@click="selectColor(color)"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<div class="color-picker-custom">
|
|
||||||
<span>自定义颜色:</span>
|
|
||||||
<input
|
|
||||||
type="color"
|
|
||||||
v-model="form.color"
|
|
||||||
@change="showColorPicker = false"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="描述" field="description">
|
|
||||||
<a-textarea
|
|
||||||
v-model="form.description"
|
|
||||||
placeholder="请输入描述"
|
|
||||||
:auto-size="{ minRows: 3, maxRows: 6 }"
|
|
||||||
:max-length="500"
|
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
<a-form-item label="备注" field="remarks">
|
<a-form-item field="code" label="分类编码" required>
|
||||||
|
<a-input
|
||||||
|
v-model="formData.code"
|
||||||
|
placeholder="请输入分类编码"
|
||||||
|
:max-length="50"
|
||||||
|
:disabled="isEdit"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item field="parent_id" label="父级分类">
|
||||||
|
<a-tree-select
|
||||||
|
v-model="formData.parent_id"
|
||||||
|
:data="categoryTreeData"
|
||||||
|
placeholder="请选择父级分类(不选则为顶级分类)"
|
||||||
|
allow-clear
|
||||||
|
:field-names="{
|
||||||
|
key: 'id',
|
||||||
|
title: 'name',
|
||||||
|
children: 'children',
|
||||||
|
}"
|
||||||
|
:load-more="handleLoadMore"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item field="sort" label="排序">
|
||||||
|
<a-input-number
|
||||||
|
v-model="formData.sort"
|
||||||
|
placeholder="请输入排序值"
|
||||||
|
:min="0"
|
||||||
|
:max="9999"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item field="color" label="颜色">
|
||||||
|
<a-input
|
||||||
|
v-model="formData.color"
|
||||||
|
placeholder="请输入颜色值,如 #1890ff"
|
||||||
|
:max-length="20"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<div
|
||||||
|
:style="{
|
||||||
|
width: '16px',
|
||||||
|
height: '16px',
|
||||||
|
borderRadius: '2px',
|
||||||
|
backgroundColor: formData.color || '#1890ff',
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</a-input>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item field="enabled" label="是否启用">
|
||||||
|
<a-switch v-model="formData.enabled" />
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item field="description" label="描述">
|
||||||
<a-textarea
|
<a-textarea
|
||||||
v-model="form.remarks"
|
v-model="formData.description"
|
||||||
|
placeholder="请输入分类描述"
|
||||||
|
:max-length="200"
|
||||||
|
:auto-size="{ minRows: 2, maxRows: 4 }"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item field="remarks" label="备注">
|
||||||
|
<a-textarea
|
||||||
|
v-model="formData.remarks"
|
||||||
placeholder="请输入备注"
|
placeholder="请输入备注"
|
||||||
:auto-size="{ minRows: 3, maxRows: 6 }"
|
:max-length="200"
|
||||||
:max-length="500"
|
:auto-size="{ minRows: 2, maxRows: 4 }"
|
||||||
|
show-word-limit
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
@@ -139,167 +98,176 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, watch, computed } from 'vue'
|
import { ref, computed, watch } from 'vue'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message } from '@arco-design/web-vue'
|
||||||
import { createCategory, updateCategory } from '@/api/ops/assetCategory'
|
import type { FormInstance } from '@arco-design/web-vue/es/form'
|
||||||
|
import {
|
||||||
interface Category {
|
createCategory,
|
||||||
id?: number
|
updateCategory,
|
||||||
name?: string
|
fetchCategoryTree,
|
||||||
code?: string
|
fetchCategoryChildren,
|
||||||
description?: string
|
} from '@/api/ops/assetCategory'
|
||||||
icon?: string
|
import type { AssetCategory, CategoryFormData } from '@/api/ops/assetCategory'
|
||||||
color?: string
|
|
||||||
parent_id?: number
|
|
||||||
sort?: number
|
|
||||||
enabled?: boolean
|
|
||||||
remarks?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible: boolean
|
visible: boolean
|
||||||
category: Category | null
|
category?: AssetCategory | null
|
||||||
parentCategories: any[]
|
|
||||||
fixedParentId?: number
|
|
||||||
showParentSelector?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'update:visible', value: boolean): void
|
(e: 'update:visible', visible: boolean): void
|
||||||
(e: 'success'): void
|
(e: 'success'): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
visible: false,
|
||||||
|
category: null,
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits<Emits>()
|
const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
const formRef = ref()
|
const formRef = ref<FormInstance>()
|
||||||
const submitting = ref(false)
|
const categoryTreeData = ref<any[]>([])
|
||||||
const showColorPicker = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
// 预设颜色
|
const isEdit = computed(() => !!props.category?.id)
|
||||||
const presetColors = [
|
|
||||||
'#FF0000', '#FF4500', '#FF8C00', '#FFD700', '#FFFF00',
|
|
||||||
'#9ACD32', '#32CD32', '#00FF00', '#00FA9A', '#00CED1',
|
|
||||||
'#1E90FF', '#0000FF', '#8A2BE2', '#9400D3', '#FF00FF',
|
|
||||||
'#FF1493', '#DC143C', '#B22222', '#8B0000', '#800000',
|
|
||||||
]
|
|
||||||
|
|
||||||
// 表单数据
|
const dialogVisible = computed({
|
||||||
const form = ref({
|
get: () => props.visible,
|
||||||
|
set: (val) => emit('update:visible', val),
|
||||||
|
})
|
||||||
|
|
||||||
|
const defaultFormData = (): CategoryFormData => ({
|
||||||
name: '',
|
name: '',
|
||||||
code: '',
|
code: '',
|
||||||
description: '',
|
description: '',
|
||||||
icon: '',
|
color: '#1890ff',
|
||||||
color: '',
|
parent_id: null,
|
||||||
parent_id: undefined as number | undefined,
|
|
||||||
sort: 0,
|
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
sort: 0,
|
||||||
remarks: '',
|
remarks: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
// 是否为编辑模式
|
const formData = ref<CategoryFormData>(defaultFormData())
|
||||||
const isEdit = computed(() => !!props.category?.id)
|
|
||||||
|
|
||||||
// 父级分类下拉搜索过滤
|
const rules = {
|
||||||
const filterParentOption = (input: string, option: any) => {
|
name: [
|
||||||
return option.label.toLowerCase().includes(input.toLowerCase())
|
{ required: true, message: '分类名称不能为空' },
|
||||||
|
{ max: 50, message: '分类名称不能超过50个字符' },
|
||||||
|
],
|
||||||
|
code: [
|
||||||
|
{ required: true, message: '分类编码不能为空' },
|
||||||
|
{ max: 50, message: '分类编码不能超过50个字符' },
|
||||||
|
{
|
||||||
|
match: /^[A-Z0-9_]+$/,
|
||||||
|
message: '分类编码只能包含大写字母、数字和下划线',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
color: [
|
||||||
|
{
|
||||||
|
match: /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/,
|
||||||
|
message: '请输入有效的颜色值,如 #1890ff',
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
// 选择颜色
|
// 加载分类树
|
||||||
const selectColor = (color: string) => {
|
const loadCategoryTree = async () => {
|
||||||
form.value.color = color
|
try {
|
||||||
showColorPicker.value = false
|
const res = await fetchCategoryTree()
|
||||||
|
categoryTreeData.value = res.details || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载分类树失败:', error)
|
||||||
|
categoryTreeData.value = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听对话框显示状态
|
// 懒加载子节点
|
||||||
|
const handleLoadMore = async (node: any) => {
|
||||||
|
if (node.children && node.children.length > 0) return
|
||||||
|
try {
|
||||||
|
const res = await fetchCategoryChildren(node.id)
|
||||||
|
node.children = res.details || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载子分类失败:', error)
|
||||||
|
node.children = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.value = defaultFormData()
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听 visible 变化
|
||||||
watch(
|
watch(
|
||||||
() => props.visible,
|
() => props.visible,
|
||||||
(newVal) => {
|
async (val) => {
|
||||||
if (newVal) {
|
if (val) {
|
||||||
if (props.category && isEdit.value) {
|
await loadCategoryTree()
|
||||||
// 编辑模式:填充表单
|
if (props.category) {
|
||||||
form.value = {
|
// 编辑模式,填充数据
|
||||||
name: props.category.name || '',
|
formData.value = {
|
||||||
code: props.category.code || '',
|
id: props.category.id,
|
||||||
|
name: props.category.name,
|
||||||
|
code: props.category.code,
|
||||||
description: props.category.description || '',
|
description: props.category.description || '',
|
||||||
icon: props.category.icon || '',
|
color: props.category.color || '#1890ff',
|
||||||
color: props.category.color || '',
|
parent_id: props.category.parent_id || null,
|
||||||
parent_id: props.category.parent_id,
|
enabled: props.category.enabled,
|
||||||
sort: props.category.sort || 0,
|
sort: props.category.sort || 0,
|
||||||
enabled: props.category.enabled !== undefined ? props.category.enabled : true,
|
|
||||||
remarks: props.category.remarks || '',
|
remarks: props.category.remarks || '',
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 新建模式:重置表单
|
resetForm()
|
||||||
form.value = {
|
|
||||||
name: '',
|
|
||||||
code: '',
|
|
||||||
description: '',
|
|
||||||
icon: '',
|
|
||||||
color: '',
|
|
||||||
parent_id: props.fixedParentId,
|
|
||||||
sort: 0,
|
|
||||||
enabled: true,
|
|
||||||
remarks: '',
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
showColorPicker.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 确认提交
|
|
||||||
const handleOk = async () => {
|
|
||||||
const valid = await formRef.value?.validate()
|
|
||||||
if (valid) return
|
|
||||||
|
|
||||||
submitting.value = true
|
|
||||||
try {
|
|
||||||
const data: any = {
|
|
||||||
name: form.value.name,
|
|
||||||
code: form.value.code,
|
|
||||||
description: form.value.description,
|
|
||||||
icon: form.value.icon,
|
|
||||||
color: form.value.color,
|
|
||||||
parent_id: form.value.parent_id,
|
|
||||||
sort: form.value.sort,
|
|
||||||
enabled: form.value.enabled,
|
|
||||||
remarks: form.value.remarks,
|
|
||||||
}
|
|
||||||
|
|
||||||
let res
|
|
||||||
if (isEdit.value && props.category?.id) {
|
|
||||||
// 编辑分类
|
|
||||||
data.id = props.category.id
|
|
||||||
res = await updateCategory(data)
|
|
||||||
} else {
|
|
||||||
// 新建分类
|
|
||||||
res = await createCategory(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.code === 0) {
|
|
||||||
Message.success(isEdit.value ? '编辑成功' : '创建成功')
|
|
||||||
emit('success')
|
|
||||||
emit('update:visible', false)
|
|
||||||
} else {
|
|
||||||
Message.error(res.message || (isEdit.value ? '编辑失败' : '创建失败'))
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
Message.error(isEdit.value ? '编辑失败' : '创建失败')
|
|
||||||
console.error(error)
|
|
||||||
} finally {
|
|
||||||
submitting.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消
|
|
||||||
const handleCancel = () => {
|
const handleCancel = () => {
|
||||||
emit('update:visible', false)
|
emit('update:visible', false)
|
||||||
|
resetForm()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理对话框可见性变化
|
const handleOk = async () => {
|
||||||
const handleVisibleChange = (visible: boolean) => {
|
try {
|
||||||
emit('update:visible', visible)
|
const valid = await formRef.value?.validate()
|
||||||
|
if (valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
const submitData = { ...formData.value }
|
||||||
|
|
||||||
|
if (isEdit.value) {
|
||||||
|
// 编辑
|
||||||
|
const res = await updateCategory(submitData)
|
||||||
|
if (res.code === 0) {
|
||||||
|
Message.success('更新成功')
|
||||||
|
emit('success')
|
||||||
|
emit('update:visible', false)
|
||||||
|
resetForm()
|
||||||
|
} else {
|
||||||
|
Message.error(res.message || '更新失败')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 新建
|
||||||
|
const res = await createCategory(submitData)
|
||||||
|
if (res.code === 0) {
|
||||||
|
Message.success('创建成功')
|
||||||
|
emit('success')
|
||||||
|
emit('update:visible', false)
|
||||||
|
resetForm()
|
||||||
|
} else {
|
||||||
|
Message.error(res.message || '创建失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('提交失败:', error)
|
||||||
|
Message.error(error.message || '操作失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -307,79 +275,4 @@ const handleVisibleChange = (visible: boolean) => {
|
|||||||
export default {
|
export default {
|
||||||
name: 'CategoryFormDialog',
|
name: 'CategoryFormDialog',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.color-picker-wrapper {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.color-preview {
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
border-radius: 2px;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-picker-dropdown {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
border-radius: 4px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
||||||
padding: 12px;
|
|
||||||
margin-top: 4px;
|
|
||||||
width: 280px;
|
|
||||||
|
|
||||||
.color-picker-header {
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
color: #262626;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-picker-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(10, 1fr);
|
|
||||||
gap: 4px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
|
|
||||||
.color-item {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.1);
|
|
||||||
border-color: #1890ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-picker-custom {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
padding-top: 8px;
|
|
||||||
border-top: 1px solid #f0f0f0;
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #595959;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="color"] {
|
|
||||||
width: 60px;
|
|
||||||
height: 28px;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
69
src/views/ops/pages/assets/classify/config/columns.ts
Normal file
69
src/views/ops/pages/assets/classify/config/columns.ts
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'
|
||||||
|
|
||||||
|
export const columns: TableColumnData[] = [
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
dataIndex: 'index',
|
||||||
|
slotName: 'index',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类编码',
|
||||||
|
dataIndex: 'code',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '层级',
|
||||||
|
dataIndex: 'level',
|
||||||
|
slotName: 'level',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '父级分类',
|
||||||
|
dataIndex: 'parent',
|
||||||
|
slotName: 'parent',
|
||||||
|
width: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '资产数量',
|
||||||
|
dataIndex: 'asset_count',
|
||||||
|
slotName: 'asset_count',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '排序',
|
||||||
|
dataIndex: 'sort',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'enabled',
|
||||||
|
slotName: 'enabled',
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '描述',
|
||||||
|
dataIndex: 'description',
|
||||||
|
ellipsis: true,
|
||||||
|
tooltip: true,
|
||||||
|
width: 200,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
slotName: 'created_at',
|
||||||
|
width: 180,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'actions',
|
||||||
|
slotName: 'actions',
|
||||||
|
width: 320,
|
||||||
|
fixed: 'right' as const,
|
||||||
|
},
|
||||||
|
]
|
||||||
10
src/views/ops/pages/assets/classify/config/search-form.ts
Normal file
10
src/views/ops/pages/assets/classify/config/search-form.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import type { FormItem } from '@/components/search-form/types'
|
||||||
|
|
||||||
|
export const searchFormConfig: FormItem[] = [
|
||||||
|
{
|
||||||
|
field: 'keyword',
|
||||||
|
label: '关键词',
|
||||||
|
type: 'input',
|
||||||
|
placeholder: '请输入分类名称或编码',
|
||||||
|
},
|
||||||
|
]
|
||||||
@@ -18,7 +18,10 @@
|
|||||||
>
|
>
|
||||||
<template #toolbar-left>
|
<template #toolbar-left>
|
||||||
<a-button type="primary" @click="handleCreate">
|
<a-button type="primary" @click="handleCreate">
|
||||||
新增分类
|
<template #icon>
|
||||||
|
<icon-plus />
|
||||||
|
</template>
|
||||||
|
新建分类
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -27,29 +30,35 @@
|
|||||||
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
|
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 层级 -->
|
||||||
|
<template #level="{ record }">
|
||||||
|
<a-tag :color="getLevelColor(record.level)">
|
||||||
|
第{{ record.level }}级
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 父级分类 -->
|
<!-- 父级分类 -->
|
||||||
<template #parent="{ record }">
|
<template #parent="{ record }">
|
||||||
{{ record.parent?.name || '-' }}
|
{{ record.parent?.name || '-' }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 颜色标识 -->
|
<!-- 资产数量 -->
|
||||||
<template #color="{ record }">
|
<template #asset_count="{ record }">
|
||||||
<div class="color-cell">
|
<a-badge :count="record.asset_count" :max-count="999" />
|
||||||
<div
|
|
||||||
class="color-block"
|
|
||||||
:style="{ backgroundColor: record.color || '#ccc' }"
|
|
||||||
></div>
|
|
||||||
<span>{{ record.color || '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 是否启用 -->
|
<!-- 状态 -->
|
||||||
<template #enabled="{ record }">
|
<template #enabled="{ record }">
|
||||||
<a-tag :color="record.enabled ? 'green' : 'red'">
|
<a-tag :color="record.enabled ? 'green' : 'red'">
|
||||||
{{ record.enabled ? '是' : '否' }}
|
{{ record.enabled ? '启用' : '禁用' }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- 创建时间 -->
|
||||||
|
<template #created_at="{ record }">
|
||||||
|
{{ formatDateTime(record.created_at) }}
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- 操作 -->
|
<!-- 操作 -->
|
||||||
<template #actions="{ record }">
|
<template #actions="{ record }">
|
||||||
<a-button type="text" size="small" @click="handleDetail(record)">
|
<a-button type="text" size="small" @click="handleDetail(record)">
|
||||||
@@ -58,29 +67,31 @@
|
|||||||
<a-button type="text" size="small" @click="handleEdit(record)">
|
<a-button type="text" size="small" @click="handleEdit(record)">
|
||||||
编辑
|
编辑
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="text" size="small" status="danger" @click="handleDelete(record)">
|
<a-button type="text" size="small" @click="handleAddChild(record)">
|
||||||
删除
|
添加子分类
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button type="text" size="small" status="success" @click="handleAddChild(record)">
|
<a-button
|
||||||
新增
|
type="text"
|
||||||
|
size="small"
|
||||||
|
status="danger"
|
||||||
|
@click="handleDelete(record)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
|
|
||||||
<!-- 分类表单对话框(新增/编辑) -->
|
<!-- 分类表单对话框 -->
|
||||||
<category-form-dialog
|
<category-form-dialog
|
||||||
v-model:visible="formVisible"
|
v-model:visible="formVisible"
|
||||||
:category="editingCategory"
|
:category="editingCategory"
|
||||||
:parent-categories="parentCategories"
|
|
||||||
:fixed-parent-id="fixedParentId"
|
|
||||||
:show-parent-selector="showParentSelector"
|
|
||||||
@success="handleFormSuccess"
|
@success="handleFormSuccess"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 分类详情对话框 -->
|
<!-- 分类详情对话框 -->
|
||||||
<category-detail-dialog
|
<category-detail-dialog
|
||||||
v-model:visible="detailVisible"
|
v-model:visible="detailVisible"
|
||||||
:category="currentCategory"
|
:category-id="currentCategoryId"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -91,21 +102,21 @@ import { Message, Modal } from '@arco-design/web-vue'
|
|||||||
import { IconPlus } from '@arco-design/web-vue/es/icon'
|
import { IconPlus } from '@arco-design/web-vue/es/icon'
|
||||||
import type { FormItem } from '@/components/search-form/types'
|
import type { FormItem } from '@/components/search-form/types'
|
||||||
import SearchTable from '@/components/search-table/index.vue'
|
import SearchTable from '@/components/search-table/index.vue'
|
||||||
|
import { searchFormConfig } from './config/search-form'
|
||||||
|
import { columns as columnsConfig } from './config/columns'
|
||||||
import {
|
import {
|
||||||
fetchCategoryList,
|
fetchCategoryList,
|
||||||
deleteCategory,
|
deleteCategory,
|
||||||
fetchAllCategories,
|
|
||||||
fetchCategoryDetail,
|
|
||||||
} from '@/api/ops/assetCategory'
|
} from '@/api/ops/assetCategory'
|
||||||
|
import type { AssetCategory } from '@/api/ops/assetCategory'
|
||||||
import CategoryFormDialog from './components/CategoryFormDialog.vue'
|
import CategoryFormDialog from './components/CategoryFormDialog.vue'
|
||||||
import CategoryDetailDialog from './components/CategoryDetailDialog.vue'
|
import CategoryDetailDialog from './components/CategoryDetailDialog.vue'
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const tableData = ref<any[]>([])
|
const tableData = ref<AssetCategory[]>([])
|
||||||
const formModel = ref({
|
const formModel = ref({
|
||||||
keyword: '',
|
keyword: '',
|
||||||
parent_id: '',
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const pagination = reactive({
|
const pagination = reactive({
|
||||||
@@ -114,114 +125,46 @@ const pagination = reactive({
|
|||||||
total: 0,
|
total: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 父级分类列表(用于下拉选择)
|
|
||||||
const parentCategories = ref<any[]>([])
|
|
||||||
|
|
||||||
// 表单项配置
|
// 表单项配置
|
||||||
const formItems = computed<FormItem[]>(() => [
|
const formItems = computed<FormItem[]>(() => searchFormConfig)
|
||||||
{
|
|
||||||
field: 'keyword',
|
|
||||||
label: '关键词',
|
|
||||||
type: 'input',
|
|
||||||
placeholder: '请输入分类名称或编码',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'parent_id',
|
|
||||||
label: '父级分类',
|
|
||||||
type: 'select',
|
|
||||||
placeholder: '请选择父级分类',
|
|
||||||
options: parentCategories.value.map((item) => ({
|
|
||||||
label: item.name,
|
|
||||||
value: item.id,
|
|
||||||
})),
|
|
||||||
allowClear: true,
|
|
||||||
allowSearch: true,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// 表格列配置
|
// 表格列配置
|
||||||
const columns = computed(() => [
|
const columns = computed(() => columnsConfig)
|
||||||
{
|
|
||||||
title: '序号',
|
|
||||||
dataIndex: 'index',
|
|
||||||
slotName: 'index',
|
|
||||||
width: 80,
|
|
||||||
align: 'center' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '分类名称',
|
|
||||||
dataIndex: 'name',
|
|
||||||
ellipsis: true,
|
|
||||||
tooltip: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '分类编码',
|
|
||||||
dataIndex: 'code',
|
|
||||||
ellipsis: true,
|
|
||||||
tooltip: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '父级分类',
|
|
||||||
dataIndex: 'parent',
|
|
||||||
slotName: 'parent',
|
|
||||||
ellipsis: true,
|
|
||||||
tooltip: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '层级',
|
|
||||||
dataIndex: 'level',
|
|
||||||
width: 80,
|
|
||||||
align: 'center' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '排序',
|
|
||||||
dataIndex: 'sort',
|
|
||||||
width: 80,
|
|
||||||
align: 'center' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '颜色标识',
|
|
||||||
dataIndex: 'color',
|
|
||||||
slotName: 'color',
|
|
||||||
width: 120,
|
|
||||||
align: 'center' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '是否启用',
|
|
||||||
dataIndex: 'enabled',
|
|
||||||
slotName: 'enabled',
|
|
||||||
width: 100,
|
|
||||||
align: 'center' as const,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作',
|
|
||||||
slotName: 'actions',
|
|
||||||
width: 300,
|
|
||||||
fixed: 'right' as const,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// 当前选中的分类
|
// 当前选中的分类
|
||||||
const currentCategory = ref<any>(null)
|
const currentCategoryId = ref<number | undefined>(undefined)
|
||||||
const editingCategory = ref<any>(null)
|
const editingCategory = ref<AssetCategory | null>(null)
|
||||||
|
|
||||||
// 对话框可见性
|
// 对话框可见性
|
||||||
const formVisible = ref(false)
|
const formVisible = ref(false)
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
|
|
||||||
// 固定的父级分类ID(用于新增子分类时)
|
// 获取层级颜色
|
||||||
const fixedParentId = ref<number | undefined>(undefined)
|
const getLevelColor = (level: number) => {
|
||||||
|
const colors: Record<number, string> = {
|
||||||
|
1: 'blue',
|
||||||
|
2: 'green',
|
||||||
|
3: 'orange',
|
||||||
|
4: 'purple',
|
||||||
|
5: 'cyan',
|
||||||
|
}
|
||||||
|
return colors[level] || 'gray'
|
||||||
|
}
|
||||||
|
|
||||||
// 是否显示父级分类选择器
|
// 格式化日期时间
|
||||||
const showParentSelector = ref(true)
|
const formatDateTime = (dateStr?: string) => {
|
||||||
|
if (!dateStr) return '-'
|
||||||
// 获取父级分类列表(用于下拉选择)
|
|
||||||
const fetchParentCategories = async () => {
|
|
||||||
try {
|
try {
|
||||||
const res = await fetchAllCategories()
|
const date = new Date(dateStr)
|
||||||
parentCategories.value = res.details || []
|
return date.toLocaleString('zh-CN', {
|
||||||
} catch (error) {
|
year: 'numeric',
|
||||||
console.error('获取父级分类列表失败:', error)
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
return dateStr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,11 +173,10 @@ const fetchCategories = async () => {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params: any = {
|
const params = {
|
||||||
page: pagination.current,
|
page: pagination.current,
|
||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
keyword: formModel.value.keyword || undefined,
|
keyword: formModel.value.keyword || undefined,
|
||||||
parent_id: formModel.value.parent_id || undefined,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await fetchCategoryList(params)
|
const res = await fetchCategoryList(params)
|
||||||
@@ -266,7 +208,6 @@ const handleFormModelUpdate = (value: any) => {
|
|||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
formModel.value = {
|
formModel.value = {
|
||||||
keyword: '',
|
keyword: '',
|
||||||
parent_id: '',
|
|
||||||
}
|
}
|
||||||
pagination.current = 1
|
pagination.current = 1
|
||||||
fetchCategories()
|
fetchCategories()
|
||||||
@@ -284,53 +225,38 @@ const handleRefresh = () => {
|
|||||||
Message.success('数据已刷新')
|
Message.success('数据已刷新')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增分类
|
// 新建分类
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
editingCategory.value = null
|
editingCategory.value = null
|
||||||
fixedParentId.value = undefined
|
|
||||||
showParentSelector.value = false
|
|
||||||
formVisible.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增子分类
|
|
||||||
const handleAddChild = (record: any) => {
|
|
||||||
editingCategory.value = null
|
|
||||||
fixedParentId.value = record.id
|
|
||||||
showParentSelector.value = true
|
|
||||||
formVisible.value = true
|
formVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑分类
|
// 编辑分类
|
||||||
const handleEdit = (record: any) => {
|
const handleEdit = (record: AssetCategory) => {
|
||||||
console.log('编辑分类:', record)
|
|
||||||
editingCategory.value = record
|
editingCategory.value = record
|
||||||
formVisible.value = true
|
formVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 添加子分类
|
||||||
|
const handleAddChild = (record: AssetCategory) => {
|
||||||
|
editingCategory.value = {
|
||||||
|
parent_id: record.id,
|
||||||
|
} as AssetCategory
|
||||||
|
formVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
// 详情
|
// 详情
|
||||||
const handleDetail = async (record: any) => {
|
const handleDetail = (record: AssetCategory) => {
|
||||||
console.log('查看详情:', record)
|
currentCategoryId.value = record.id
|
||||||
try {
|
detailVisible.value = true
|
||||||
const res = await fetchCategoryDetail(record.id)
|
|
||||||
if (res.code === 0) {
|
|
||||||
currentCategory.value = res.details
|
|
||||||
detailVisible.value = true
|
|
||||||
} else {
|
|
||||||
Message.error(res.message || '获取详情失败')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取分类详情失败:', error)
|
|
||||||
Message.error('获取详情失败')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除分类
|
// 删除分类
|
||||||
const handleDelete = async (record: any) => {
|
const handleDelete = async (record: AssetCategory) => {
|
||||||
console.log('删除分类:', record)
|
|
||||||
try {
|
try {
|
||||||
Modal.confirm({
|
Modal.confirm({
|
||||||
title: '确认删除',
|
title: '确认删除',
|
||||||
content: `确认删除分类 ${record.name} 吗?`,
|
content: `确认删除分类「${record.name}」吗?如果该分类下存在子分类或资产,将无法删除。`,
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
const res = await deleteCategory(record.id)
|
const res = await deleteCategory(record.id)
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
@@ -348,21 +274,18 @@ const handleDelete = async (record: any) => {
|
|||||||
|
|
||||||
// 表单成功回调
|
// 表单成功回调
|
||||||
const handleFormSuccess = () => {
|
const handleFormSuccess = () => {
|
||||||
formVisible.value = false
|
|
||||||
fetchCategories()
|
fetchCategories()
|
||||||
fetchParentCategories()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化加载数据
|
// 初始化加载数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchParentCategories()
|
|
||||||
fetchCategories()
|
fetchCategories()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'AssetClassify',
|
name: 'AssetCategory',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -370,18 +293,4 @@ export default {
|
|||||||
.container {
|
.container {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
</style>
|
||||||
.color-cell {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 8px;
|
|
||||||
|
|
||||||
.color-block {
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #d9d9d9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,687 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-modal
|
|
||||||
:visible="visible"
|
|
||||||
:title="isEdit ? '编辑设备' : '新增设备'"
|
|
||||||
width="900px"
|
|
||||||
@ok="handleOk"
|
|
||||||
@cancel="handleCancel"
|
|
||||||
@update:visible="handleVisibleChange"
|
|
||||||
:confirm-loading="submitting"
|
|
||||||
>
|
|
||||||
<a-form :model="form" layout="vertical" ref="formRef">
|
|
||||||
<a-tabs v-model:active-key="activeTab">
|
|
||||||
<!-- 基本信息 -->
|
|
||||||
<a-tab-pane key="basic" title="基本信息">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="资产名称"
|
|
||||||
field="asset_name"
|
|
||||||
:rules="[{ required: true, message: '请输入资产名称' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.asset_name"
|
|
||||||
placeholder="请输入资产名称"
|
|
||||||
:max-length="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="资产编号"
|
|
||||||
field="asset_code"
|
|
||||||
:rules="[{ required: true, message: '请输入资产编号' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.asset_code"
|
|
||||||
placeholder="请输入资产编号"
|
|
||||||
:max-length="100"
|
|
||||||
:disabled="isEdit"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="资产分类" field="category_id">
|
|
||||||
<a-select
|
|
||||||
v-model="form.category_id"
|
|
||||||
placeholder="请选择资产分类"
|
|
||||||
allow-clear
|
|
||||||
allow-search
|
|
||||||
:loading="categoryLoading"
|
|
||||||
>
|
|
||||||
<a-option
|
|
||||||
v-for="item in categoryOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:value="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
/>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="规格型号" field="model">
|
|
||||||
<a-input
|
|
||||||
v-model="form.model"
|
|
||||||
placeholder="请输入规格型号"
|
|
||||||
:max-length="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="生产厂商" field="manufacturer">
|
|
||||||
<a-input
|
|
||||||
v-model="form.manufacturer"
|
|
||||||
placeholder="请输入生产厂商"
|
|
||||||
:max-length="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="序列号" field="serial_number">
|
|
||||||
<a-input
|
|
||||||
v-model="form.serial_number"
|
|
||||||
placeholder="请输入序列号"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="采购日期" field="purchase_date">
|
|
||||||
<a-date-picker
|
|
||||||
v-model="form.purchase_date"
|
|
||||||
placeholder="请选择采购日期"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="原值(元)" field="original_value">
|
|
||||||
<a-input-number
|
|
||||||
v-model="form.original_value"
|
|
||||||
placeholder="请输入原值"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="供应商" field="supplier_id">
|
|
||||||
<a-select
|
|
||||||
v-model="form.supplier_id"
|
|
||||||
placeholder="请选择供应商"
|
|
||||||
allow-clear
|
|
||||||
allow-search
|
|
||||||
:loading="supplierLoading"
|
|
||||||
>
|
|
||||||
<a-option
|
|
||||||
v-for="item in supplierOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:value="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
/>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="质保期" field="warranty_period">
|
|
||||||
<a-input
|
|
||||||
v-model="form.warranty_period"
|
|
||||||
placeholder="请输入质保期(如:36个月)"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="质保到期日期" field="warranty_expiry">
|
|
||||||
<a-date-picker
|
|
||||||
v-model="form.warranty_expiry"
|
|
||||||
placeholder="请选择质保到期日期"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="资产状态" field="status">
|
|
||||||
<a-select
|
|
||||||
v-model="form.status"
|
|
||||||
placeholder="请选择资产状态"
|
|
||||||
>
|
|
||||||
<a-option
|
|
||||||
v-for="item in assetStatusOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:value="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
/>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<!-- 使用信息 -->
|
|
||||||
<a-tab-pane key="usage" title="使用信息">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="使用部门" field="department">
|
|
||||||
<a-input
|
|
||||||
v-model="form.department"
|
|
||||||
placeholder="请输入使用部门"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="使用人" field="user">
|
|
||||||
<a-input
|
|
||||||
v-model="form.user"
|
|
||||||
placeholder="请输入使用人"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<!-- 位置信息 -->
|
|
||||||
<a-tab-pane key="location" title="位置信息">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="所属数据中心" field="datacenter_id">
|
|
||||||
<a-select
|
|
||||||
v-model="form.datacenter_id"
|
|
||||||
placeholder="请选择数据中心"
|
|
||||||
allow-clear
|
|
||||||
allow-search
|
|
||||||
:loading="datacenterLoading"
|
|
||||||
@change="handleDatacenterChange"
|
|
||||||
>
|
|
||||||
<a-option
|
|
||||||
v-for="item in datacenterOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:value="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
/>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="所属楼层" field="floor_id">
|
|
||||||
<a-select
|
|
||||||
v-model="form.floor_id"
|
|
||||||
placeholder="请选择楼层"
|
|
||||||
allow-clear
|
|
||||||
allow-search
|
|
||||||
:loading="floorLoading"
|
|
||||||
:disabled="!form.datacenter_id"
|
|
||||||
@change="handleFloorChange"
|
|
||||||
>
|
|
||||||
<a-option
|
|
||||||
v-for="item in floorOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:value="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
/>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="所属机柜" field="rack_id">
|
|
||||||
<a-select
|
|
||||||
v-model="form.rack_id"
|
|
||||||
placeholder="请选择机柜"
|
|
||||||
allow-clear
|
|
||||||
allow-search
|
|
||||||
:loading="rackLoading"
|
|
||||||
:disabled="!form.floor_id"
|
|
||||||
>
|
|
||||||
<a-option
|
|
||||||
v-for="item in rackOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:value="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
/>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="6">
|
|
||||||
<a-form-item label="起始U位编号" field="unit_start">
|
|
||||||
<a-input-number
|
|
||||||
v-model="form.unit_start"
|
|
||||||
placeholder="起始U位"
|
|
||||||
:min="1"
|
|
||||||
:max="48"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="6">
|
|
||||||
<a-form-item label="结束U位编号" field="unit_end">
|
|
||||||
<a-input-number
|
|
||||||
v-model="form.unit_end"
|
|
||||||
placeholder="结束U位"
|
|
||||||
:min="1"
|
|
||||||
:max="48"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<!-- 其他信息 -->
|
|
||||||
<a-tab-pane key="other" title="其他信息">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="二维码标签" field="qr_code">
|
|
||||||
<a-input
|
|
||||||
v-model="form.qr_code"
|
|
||||||
placeholder="请输入二维码标签"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="RFID标签" field="rfid_tag">
|
|
||||||
<a-input
|
|
||||||
v-model="form.rfid_tag"
|
|
||||||
placeholder="请输入RFID标签"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="资产标签号" field="asset_tag">
|
|
||||||
<a-input
|
|
||||||
v-model="form.asset_tag"
|
|
||||||
placeholder="请输入资产标签号"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="技术规格" field="specifications">
|
|
||||||
<a-input
|
|
||||||
v-model="form.specifications"
|
|
||||||
placeholder="请输入技术规格"
|
|
||||||
:max-length="500"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-form-item label="资产描述" field="description">
|
|
||||||
<a-textarea
|
|
||||||
v-model="form.description"
|
|
||||||
placeholder="请输入资产描述"
|
|
||||||
:auto-size="{ minRows: 3, maxRows: 6 }"
|
|
||||||
:max-length="1000"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
|
|
||||||
<a-form-item label="备注" field="remarks">
|
|
||||||
<a-textarea
|
|
||||||
v-model="form.remarks"
|
|
||||||
placeholder="请输入备注"
|
|
||||||
:auto-size="{ minRows: 3, maxRows: 6 }"
|
|
||||||
:max-length="500"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-tab-pane>
|
|
||||||
</a-tabs>
|
|
||||||
</a-form>
|
|
||||||
</a-modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, watch, computed, onMounted } from 'vue'
|
|
||||||
import { Message } from '@arco-design/web-vue'
|
|
||||||
import {
|
|
||||||
createAsset,
|
|
||||||
updateAsset,
|
|
||||||
fetchCategoryOptions,
|
|
||||||
fetchSupplierOptions,
|
|
||||||
fetchDatacenterOptions,
|
|
||||||
fetchFloorOptions,
|
|
||||||
fetchRackOptions,
|
|
||||||
assetStatusOptions,
|
|
||||||
AssetForm,
|
|
||||||
} from '@/api/ops/asset'
|
|
||||||
|
|
||||||
interface Device {
|
|
||||||
id?: number
|
|
||||||
asset_name?: string
|
|
||||||
asset_code?: string
|
|
||||||
category_id?: number
|
|
||||||
model?: string
|
|
||||||
manufacturer?: string
|
|
||||||
serial_number?: string
|
|
||||||
purchase_date?: string
|
|
||||||
original_value?: number
|
|
||||||
supplier_id?: number
|
|
||||||
warranty_period?: string
|
|
||||||
warranty_expiry?: string
|
|
||||||
department?: string
|
|
||||||
user?: string
|
|
||||||
status?: string
|
|
||||||
location?: string
|
|
||||||
datacenter_id?: number
|
|
||||||
floor_id?: number
|
|
||||||
rack_id?: number
|
|
||||||
unit_start?: number
|
|
||||||
unit_end?: number
|
|
||||||
qr_code?: string
|
|
||||||
rfid_tag?: string
|
|
||||||
asset_tag?: string
|
|
||||||
specifications?: string
|
|
||||||
description?: string
|
|
||||||
remarks?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
visible: boolean
|
|
||||||
device: Device | null
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'update:visible', value: boolean): void
|
|
||||||
(e: 'success'): void
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
|
||||||
const emit = defineEmits<Emits>()
|
|
||||||
|
|
||||||
const formRef = ref()
|
|
||||||
const submitting = ref(false)
|
|
||||||
const activeTab = ref('basic')
|
|
||||||
|
|
||||||
// 下拉选项
|
|
||||||
const categoryOptions = ref<any[]>([])
|
|
||||||
const supplierOptions = ref<any[]>([])
|
|
||||||
const datacenterOptions = ref<any[]>([])
|
|
||||||
const floorOptions = ref<any[]>([])
|
|
||||||
const rackOptions = ref<any[]>([])
|
|
||||||
|
|
||||||
// 加载状态
|
|
||||||
const categoryLoading = ref(false)
|
|
||||||
const supplierLoading = ref(false)
|
|
||||||
const datacenterLoading = ref(false)
|
|
||||||
const floorLoading = ref(false)
|
|
||||||
const rackLoading = ref(false)
|
|
||||||
|
|
||||||
// 是否为编辑模式
|
|
||||||
const isEdit = computed(() => !!props.device?.id)
|
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const form = ref<AssetForm>({
|
|
||||||
asset_name: '',
|
|
||||||
asset_code: '',
|
|
||||||
category_id: undefined,
|
|
||||||
model: '',
|
|
||||||
manufacturer: '',
|
|
||||||
serial_number: '',
|
|
||||||
purchase_date: '',
|
|
||||||
original_value: undefined,
|
|
||||||
supplier_id: undefined,
|
|
||||||
warranty_period: '',
|
|
||||||
warranty_expiry: '',
|
|
||||||
department: '',
|
|
||||||
user: '',
|
|
||||||
status: 'in_use',
|
|
||||||
location: '',
|
|
||||||
datacenter_id: undefined,
|
|
||||||
floor_id: undefined,
|
|
||||||
rack_id: undefined,
|
|
||||||
unit_start: undefined,
|
|
||||||
unit_end: undefined,
|
|
||||||
qr_code: '',
|
|
||||||
rfid_tag: '',
|
|
||||||
asset_tag: '',
|
|
||||||
specifications: '',
|
|
||||||
description: '',
|
|
||||||
remarks: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
// 加载下拉选项
|
|
||||||
const loadOptions = async () => {
|
|
||||||
// 加载资产分类
|
|
||||||
categoryLoading.value = true
|
|
||||||
try {
|
|
||||||
const res = await fetchCategoryOptions()
|
|
||||||
if (res.code === 0) {
|
|
||||||
categoryOptions.value = res.details || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载资产分类失败:', error)
|
|
||||||
} finally {
|
|
||||||
categoryLoading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载供应商
|
|
||||||
supplierLoading.value = true
|
|
||||||
try {
|
|
||||||
const res = await fetchSupplierOptions()
|
|
||||||
if (res.code === 0) {
|
|
||||||
supplierOptions.value = res.details || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载供应商失败:', error)
|
|
||||||
} finally {
|
|
||||||
supplierLoading.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载数据中心
|
|
||||||
datacenterLoading.value = true
|
|
||||||
try {
|
|
||||||
const res = await fetchDatacenterOptions()
|
|
||||||
if (res.code === 0) {
|
|
||||||
datacenterOptions.value = res.details?.data || res.details || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载数据中心失败:', error)
|
|
||||||
} finally {
|
|
||||||
datacenterLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据中心变化时加载楼层
|
|
||||||
const handleDatacenterChange = async (value: number | undefined) => {
|
|
||||||
form.value.floor_id = undefined
|
|
||||||
form.value.rack_id = undefined
|
|
||||||
floorOptions.value = []
|
|
||||||
rackOptions.value = []
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
floorLoading.value = true
|
|
||||||
try {
|
|
||||||
const res = await fetchFloorOptions(value)
|
|
||||||
if (res.code === 0) {
|
|
||||||
floorOptions.value = res.details || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载楼层失败:', error)
|
|
||||||
} finally {
|
|
||||||
floorLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 楼层变化时加载机柜
|
|
||||||
const handleFloorChange = async (value: number | undefined) => {
|
|
||||||
form.value.rack_id = undefined
|
|
||||||
rackOptions.value = []
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
rackLoading.value = true
|
|
||||||
try {
|
|
||||||
const res = await fetchRackOptions({
|
|
||||||
datacenter_id: form.value.datacenter_id,
|
|
||||||
floor_id: value,
|
|
||||||
})
|
|
||||||
if (res.code === 0) {
|
|
||||||
rackOptions.value = res.details?.data || res.details || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载机柜失败:', error)
|
|
||||||
} finally {
|
|
||||||
rackLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听 device 变化,初始化表单
|
|
||||||
watch(
|
|
||||||
() => props.device,
|
|
||||||
async (newVal) => {
|
|
||||||
if (newVal) {
|
|
||||||
form.value = { ...newVal } as AssetForm
|
|
||||||
|
|
||||||
// 如果有数据中心,加载楼层
|
|
||||||
if (newVal.datacenter_id) {
|
|
||||||
try {
|
|
||||||
const res = await fetchFloorOptions(newVal.datacenter_id)
|
|
||||||
if (res.code === 0) {
|
|
||||||
floorOptions.value = res.details || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载楼层失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果有楼层,加载机柜
|
|
||||||
if (newVal.floor_id) {
|
|
||||||
try {
|
|
||||||
const res = await fetchRackOptions({
|
|
||||||
datacenter_id: newVal.datacenter_id,
|
|
||||||
floor_id: newVal.floor_id,
|
|
||||||
})
|
|
||||||
if (res.code === 0) {
|
|
||||||
rackOptions.value = res.details?.data || res.details || []
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载机柜失败:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 重置表单
|
|
||||||
form.value = {
|
|
||||||
asset_name: '',
|
|
||||||
asset_code: '',
|
|
||||||
category_id: undefined,
|
|
||||||
model: '',
|
|
||||||
manufacturer: '',
|
|
||||||
serial_number: '',
|
|
||||||
purchase_date: '',
|
|
||||||
original_value: undefined,
|
|
||||||
supplier_id: undefined,
|
|
||||||
warranty_period: '',
|
|
||||||
warranty_expiry: '',
|
|
||||||
department: '',
|
|
||||||
user: '',
|
|
||||||
status: 'in_use',
|
|
||||||
location: '',
|
|
||||||
datacenter_id: undefined,
|
|
||||||
floor_id: undefined,
|
|
||||||
rack_id: undefined,
|
|
||||||
unit_start: undefined,
|
|
||||||
unit_end: undefined,
|
|
||||||
qr_code: '',
|
|
||||||
rfid_tag: '',
|
|
||||||
asset_tag: '',
|
|
||||||
specifications: '',
|
|
||||||
description: '',
|
|
||||||
remarks: '',
|
|
||||||
}
|
|
||||||
floorOptions.value = []
|
|
||||||
rackOptions.value = []
|
|
||||||
}
|
|
||||||
activeTab.value = 'basic'
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// 提交表单
|
|
||||||
const handleOk = async () => {
|
|
||||||
try {
|
|
||||||
const valid = await formRef.value?.validate()
|
|
||||||
if (valid) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
submitting.value = true
|
|
||||||
|
|
||||||
const data = { ...form.value }
|
|
||||||
|
|
||||||
let res
|
|
||||||
if (isEdit.value) {
|
|
||||||
res = await updateAsset({ ...data, id: props.device?.id })
|
|
||||||
} else {
|
|
||||||
res = await createAsset(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.code === 0) {
|
|
||||||
Message.success(isEdit.value ? '更新成功' : '创建成功')
|
|
||||||
emit('success')
|
|
||||||
} else {
|
|
||||||
Message.error(res.message || '操作失败')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('提交表单失败:', error)
|
|
||||||
Message.error('操作失败')
|
|
||||||
} finally {
|
|
||||||
submitting.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消
|
|
||||||
const handleCancel = () => {
|
|
||||||
emit('update:visible', false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理对话框可见性变化
|
|
||||||
const handleVisibleChange = (visible: boolean) => {
|
|
||||||
emit('update:visible', visible)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化
|
|
||||||
onMounted(() => {
|
|
||||||
loadOptions()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
name: 'DeviceFormDialog',
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
:deep(.arco-tabs-content) {
|
|
||||||
padding-top: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
794
src/views/ops/pages/assets/device/form/index.vue
Normal file
794
src/views/ops/pages/assets/device/form/index.vue
Normal file
@@ -0,0 +1,794 @@
|
|||||||
|
<template>
|
||||||
|
<div class="device-form-page">
|
||||||
|
<!-- 页面头部 -->
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-left">
|
||||||
|
<a-button type="text" @click="handleBack">
|
||||||
|
<template #icon><icon-left /></template>
|
||||||
|
返回列表
|
||||||
|
</a-button>
|
||||||
|
<a-divider direction="vertical" />
|
||||||
|
<h2 class="page-title">{{ pageTitle }}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="header-right">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleBack">取消</a-button>
|
||||||
|
<a-button type="primary" :loading="submitting" @click="handleSubmit">
|
||||||
|
{{ isEdit ? '保存' : '创建' }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单内容 -->
|
||||||
|
<div class="page-content">
|
||||||
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
|
<a-form ref="formRef" :model="form" layout="vertical">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<a-card class="info-card" title="基本信息">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="资产名称" field="asset_name" required>
|
||||||
|
<a-input
|
||||||
|
v-model="form.asset_name"
|
||||||
|
placeholder="请输入资产名称"
|
||||||
|
:max-length="200"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="资产编号" field="asset_code" required>
|
||||||
|
<a-input
|
||||||
|
v-model="form.asset_code"
|
||||||
|
placeholder="请输入资产编号"
|
||||||
|
:max-length="100"
|
||||||
|
:disabled="isEdit"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="资产分类" field="category_id">
|
||||||
|
<a-select
|
||||||
|
v-model="form.category_id"
|
||||||
|
placeholder="请选择资产分类"
|
||||||
|
allow-clear
|
||||||
|
allow-search
|
||||||
|
:loading="categoryLoading"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="item in categoryOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
/>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="规格型号" field="model">
|
||||||
|
<a-input
|
||||||
|
v-model="form.model"
|
||||||
|
placeholder="请输入规格型号"
|
||||||
|
:max-length="200"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="生产厂商" field="manufacturer">
|
||||||
|
<a-input
|
||||||
|
v-model="form.manufacturer"
|
||||||
|
placeholder="请输入生产厂商"
|
||||||
|
:max-length="200"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="序列号" field="serial_number">
|
||||||
|
<a-input
|
||||||
|
v-model="form.serial_number"
|
||||||
|
placeholder="请输入序列号"
|
||||||
|
:max-length="100"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="采购日期" field="purchase_date">
|
||||||
|
<a-date-picker
|
||||||
|
v-model="form.purchase_date"
|
||||||
|
placeholder="请选择采购日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="原值(元)" field="original_value">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.original_value"
|
||||||
|
placeholder="请输入原值"
|
||||||
|
:min="0"
|
||||||
|
:precision="2"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="供应商" field="supplier_id">
|
||||||
|
<a-select
|
||||||
|
v-model="form.supplier_id"
|
||||||
|
placeholder="请选择供应商"
|
||||||
|
allow-clear
|
||||||
|
allow-search
|
||||||
|
:loading="supplierLoading"
|
||||||
|
:filter-option="false"
|
||||||
|
@search="handleSupplierSearch"
|
||||||
|
@dropdown-visible-change="handleSupplierDropdownChange"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="item in supplierOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
/>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="质保期" field="warranty_period">
|
||||||
|
<a-input
|
||||||
|
v-model="form.warranty_period"
|
||||||
|
placeholder="请输入质保期(如:36个月)"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="质保到期日期" field="warranty_expiry">
|
||||||
|
<a-date-picker
|
||||||
|
v-model="form.warranty_expiry"
|
||||||
|
placeholder="请选择质保到期日期"
|
||||||
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="资产状态" field="status">
|
||||||
|
<a-select v-model="form.status" placeholder="请选择资产状态">
|
||||||
|
<a-option
|
||||||
|
v-for="item in assetStatusOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 使用信息 -->
|
||||||
|
<a-card class="info-card" title="使用信息">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="使用部门" field="department">
|
||||||
|
<a-input
|
||||||
|
v-model="form.department"
|
||||||
|
placeholder="请输入使用部门"
|
||||||
|
:max-length="100"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="使用人" field="user">
|
||||||
|
<a-input
|
||||||
|
v-model="form.user"
|
||||||
|
placeholder="请输入使用人"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 位置信息 -->
|
||||||
|
<a-card class="info-card" title="位置信息">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="所属数据中心" field="datacenter_id">
|
||||||
|
<a-select
|
||||||
|
v-model="form.datacenter_id"
|
||||||
|
placeholder="请选择数据中心"
|
||||||
|
allow-clear
|
||||||
|
allow-search
|
||||||
|
:loading="datacenterLoading"
|
||||||
|
:filter-option="false"
|
||||||
|
@search="handleDatacenterSearch"
|
||||||
|
@change="handleDatacenterChange"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="item in datacenterOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="所属楼层" field="floor_id">
|
||||||
|
<a-select
|
||||||
|
v-model="form.floor_id"
|
||||||
|
placeholder="请选择楼层"
|
||||||
|
allow-clear
|
||||||
|
allow-search
|
||||||
|
:loading="floorLoading"
|
||||||
|
:disabled="!form.datacenter_id"
|
||||||
|
:filter-option="false"
|
||||||
|
@search="handleFloorSearch"
|
||||||
|
@change="handleFloorChange"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="item in floorOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:value="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
/>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="所属机柜" field="rack_id">
|
||||||
|
<a-select
|
||||||
|
v-model="form.rack_id"
|
||||||
|
placeholder="请选择机柜"
|
||||||
|
allow-clear
|
||||||
|
allow-search
|
||||||
|
:loading="rackLoading"
|
||||||
|
:disabled="!form.floor_id"
|
||||||
|
:filter-option="false"
|
||||||
|
@search="handleRackSearch"
|
||||||
|
>
|
||||||
|
<a-option
|
||||||
|
v-for="item in rackOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:value="item.id"
|
||||||
|
>
|
||||||
|
{{ item.name }} ({{ item.code }})
|
||||||
|
</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
<a-form-item label="起始U位" field="unit_start">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.unit_start"
|
||||||
|
placeholder="起始U位"
|
||||||
|
:min="1"
|
||||||
|
:max="48"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="3">
|
||||||
|
<a-form-item label="结束U位" field="unit_end">
|
||||||
|
<a-input-number
|
||||||
|
v-model="form.unit_end"
|
||||||
|
placeholder="结束U位"
|
||||||
|
:min="1"
|
||||||
|
:max="48"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 其他信息 -->
|
||||||
|
<a-card class="info-card" title="其他信息">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="二维码标签" field="qr_code">
|
||||||
|
<a-input
|
||||||
|
v-model="form.qr_code"
|
||||||
|
placeholder="请输入二维码标签"
|
||||||
|
:max-length="100"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="RFID标签" field="rfid_tag">
|
||||||
|
<a-input
|
||||||
|
v-model="form.rfid_tag"
|
||||||
|
placeholder="请输入RFID标签"
|
||||||
|
:max-length="100"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="资产标签号" field="asset_tag">
|
||||||
|
<a-input
|
||||||
|
v-model="form.asset_tag"
|
||||||
|
placeholder="请输入资产标签号"
|
||||||
|
:max-length="100"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="技术规格" field="specifications">
|
||||||
|
<a-input
|
||||||
|
v-model="form.specifications"
|
||||||
|
placeholder="请输入技术规格"
|
||||||
|
:max-length="500"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="资产描述" field="description">
|
||||||
|
<a-textarea
|
||||||
|
v-model="form.description"
|
||||||
|
placeholder="请输入资产描述"
|
||||||
|
:auto-size="{ minRows: 3, maxRows: 6 }"
|
||||||
|
:max-length="1000"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="备注" field="remarks">
|
||||||
|
<a-textarea
|
||||||
|
v-model="form.remarks"
|
||||||
|
placeholder="请输入备注"
|
||||||
|
:auto-size="{ minRows: 3, maxRows: 6 }"
|
||||||
|
:max-length="500"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import type { FormInstance } from '@arco-design/web-vue/es/form'
|
||||||
|
import {
|
||||||
|
createAsset,
|
||||||
|
updateAsset,
|
||||||
|
fetchAssetDetail,
|
||||||
|
fetchCategoryOptions,
|
||||||
|
assetStatusOptions,
|
||||||
|
AssetForm,
|
||||||
|
} from '@/api/ops/asset'
|
||||||
|
import { fetchAllSuppliers } from '@/api/ops/supplier'
|
||||||
|
import { fetchDatacenterList, fetchRackListByFloor } from '@/api/ops/rack'
|
||||||
|
import { fetchFloorListByDatacenter } from '@/api/ops/floor'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// 页面状态
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const deviceId = ref<number | null>(null)
|
||||||
|
const pageTitle = ref('新增设备')
|
||||||
|
const loading = ref(false)
|
||||||
|
const submitting = ref(false)
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 下拉选项
|
||||||
|
const categoryOptions = ref<any[]>([])
|
||||||
|
const supplierOptions = ref<any[]>([])
|
||||||
|
const datacenterOptions = ref<{ label: string; value: number }[]>([])
|
||||||
|
const floorOptions = ref<{ label: string; value: number }[]>([])
|
||||||
|
const rackOptions = ref<any[]>([])
|
||||||
|
|
||||||
|
// 加载状态
|
||||||
|
const categoryLoading = ref(false)
|
||||||
|
const supplierLoading = ref(false)
|
||||||
|
const datacenterLoading = ref(false)
|
||||||
|
const floorLoading = ref(false)
|
||||||
|
const rackLoading = ref(false)
|
||||||
|
|
||||||
|
// 搜索防抖定时器
|
||||||
|
let datacenterSearchTimer: number | undefined
|
||||||
|
let floorSearchTimer: number | undefined
|
||||||
|
let rackSearchTimer: number | undefined
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = ref<AssetForm>({
|
||||||
|
asset_name: '',
|
||||||
|
asset_code: '',
|
||||||
|
category_id: undefined,
|
||||||
|
model: '',
|
||||||
|
manufacturer: '',
|
||||||
|
serial_number: '',
|
||||||
|
purchase_date: '',
|
||||||
|
original_value: undefined,
|
||||||
|
supplier_id: undefined,
|
||||||
|
warranty_period: '',
|
||||||
|
warranty_expiry: '',
|
||||||
|
department: '',
|
||||||
|
user: '',
|
||||||
|
status: 'in_use',
|
||||||
|
location: '',
|
||||||
|
datacenter_id: undefined,
|
||||||
|
floor_id: undefined,
|
||||||
|
rack_id: undefined,
|
||||||
|
unit_start: undefined,
|
||||||
|
unit_end: undefined,
|
||||||
|
qr_code: '',
|
||||||
|
rfid_tag: '',
|
||||||
|
asset_tag: '',
|
||||||
|
specifications: '',
|
||||||
|
description: '',
|
||||||
|
remarks: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 提取列表数据的辅助函数
|
||||||
|
const extractList = (res: any): any[] => {
|
||||||
|
const candidate =
|
||||||
|
res?.data?.data ?? res?.details?.data ?? res?.data ?? res?.details ?? []
|
||||||
|
return Array.isArray(candidate) ? candidate : []
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载下拉选项
|
||||||
|
const loadOptions = async () => {
|
||||||
|
// 加载资产分类
|
||||||
|
categoryLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await fetchCategoryOptions()
|
||||||
|
if (res.code === 0) {
|
||||||
|
categoryOptions.value = res.details || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载资产分类失败:', error)
|
||||||
|
} finally {
|
||||||
|
categoryLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载全部数据中心(默认列出全部,支持模糊搜索)
|
||||||
|
await fetchDatacenters()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载供应商选项(默认20条,搜索时全部)
|
||||||
|
const loadSupplierOptions = async (keyword?: string) => {
|
||||||
|
supplierLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await fetchAllSuppliers(keyword, 'active', keyword ? undefined : 20)
|
||||||
|
if (res.code === 0) {
|
||||||
|
supplierOptions.value = res.details?.data || res.details || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载供应商失败:', error)
|
||||||
|
} finally {
|
||||||
|
supplierLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 供应商搜索
|
||||||
|
const handleSupplierSearch = (keyword: string) => {
|
||||||
|
loadSupplierOptions(keyword)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 供应商下拉框展开时加载
|
||||||
|
const handleSupplierDropdownChange = (visible: boolean) => {
|
||||||
|
if (visible && supplierOptions.value.length === 0) {
|
||||||
|
loadSupplierOptions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载数据中心列表
|
||||||
|
const fetchDatacenters = async (keyword?: string) => {
|
||||||
|
datacenterLoading.value = true
|
||||||
|
try {
|
||||||
|
const res: any = await fetchDatacenterList({ keyword })
|
||||||
|
const rows = extractList(res)
|
||||||
|
datacenterOptions.value = rows.map((d: any) => ({
|
||||||
|
label: d.name || d.code || String(d.id),
|
||||||
|
value: d.id,
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取数据中心列表失败:', error)
|
||||||
|
datacenterOptions.value = []
|
||||||
|
} finally {
|
||||||
|
datacenterLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据中心搜索(带防抖)
|
||||||
|
const handleDatacenterSearch = (keyword: string) => {
|
||||||
|
if (datacenterSearchTimer) {
|
||||||
|
window.clearTimeout(datacenterSearchTimer)
|
||||||
|
}
|
||||||
|
datacenterSearchTimer = window.setTimeout(() => {
|
||||||
|
fetchDatacenters(keyword?.trim() || undefined)
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载楼层列表
|
||||||
|
const fetchFloors = async (keyword?: string) => {
|
||||||
|
if (!form.value.datacenter_id) {
|
||||||
|
floorOptions.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
floorLoading.value = true
|
||||||
|
try {
|
||||||
|
const res: any = await fetchFloorListByDatacenter(form.value.datacenter_id, {
|
||||||
|
name: keyword || undefined,
|
||||||
|
})
|
||||||
|
const rows = extractList(res)
|
||||||
|
floorOptions.value = rows.map((floor: any) => ({
|
||||||
|
label: floor.name || floor.code || String(floor.id),
|
||||||
|
value: floor.id,
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取楼层列表失败:', error)
|
||||||
|
floorOptions.value = []
|
||||||
|
} finally {
|
||||||
|
floorLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 楼层搜索(带防抖)
|
||||||
|
const handleFloorSearch = (keyword: string) => {
|
||||||
|
if (!form.value.datacenter_id) return
|
||||||
|
if (floorSearchTimer) {
|
||||||
|
window.clearTimeout(floorSearchTimer)
|
||||||
|
}
|
||||||
|
floorSearchTimer = window.setTimeout(() => {
|
||||||
|
fetchFloors(keyword?.trim() || undefined)
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载机柜列表
|
||||||
|
const fetchRacks = async (keyword?: string) => {
|
||||||
|
if (!form.value.floor_id) {
|
||||||
|
rackOptions.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rackLoading.value = true
|
||||||
|
try {
|
||||||
|
const res: any = await fetchRackListByFloor(form.value.floor_id, { name: keyword })
|
||||||
|
rackOptions.value = extractList(res)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取机柜列表失败:', error)
|
||||||
|
rackOptions.value = []
|
||||||
|
} finally {
|
||||||
|
rackLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 机柜搜索(带防抖)
|
||||||
|
const handleRackSearch = (keyword: string) => {
|
||||||
|
if (!form.value.floor_id) return
|
||||||
|
if (rackSearchTimer) {
|
||||||
|
window.clearTimeout(rackSearchTimer)
|
||||||
|
}
|
||||||
|
rackSearchTimer = window.setTimeout(() => {
|
||||||
|
fetchRacks(keyword?.trim() || undefined)
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据中心变化时加载楼层
|
||||||
|
const handleDatacenterChange = async (value: number | undefined) => {
|
||||||
|
form.value.floor_id = undefined
|
||||||
|
form.value.rack_id = undefined
|
||||||
|
floorOptions.value = []
|
||||||
|
rackOptions.value = []
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
await fetchFloors()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 楼层变化时加载机柜
|
||||||
|
const handleFloorChange = async (value: number | undefined) => {
|
||||||
|
form.value.rack_id = undefined
|
||||||
|
rackOptions.value = []
|
||||||
|
|
||||||
|
if (value) {
|
||||||
|
await fetchRacks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化页面
|
||||||
|
const initPage = async () => {
|
||||||
|
await loadOptions()
|
||||||
|
|
||||||
|
const id = route.query.id
|
||||||
|
if (id) {
|
||||||
|
isEdit.value = true
|
||||||
|
deviceId.value = Number(id)
|
||||||
|
pageTitle.value = '编辑设备'
|
||||||
|
await loadDeviceDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载设备详情
|
||||||
|
const loadDeviceDetail = async () => {
|
||||||
|
if (!deviceId.value) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await fetchAssetDetail(deviceId.value)
|
||||||
|
if (res.code === 0) {
|
||||||
|
form.value = { ...res.details } as AssetForm
|
||||||
|
|
||||||
|
// 如果有供应商,加载供应商选项
|
||||||
|
if (res.details.supplier_id) {
|
||||||
|
try {
|
||||||
|
const supplierRes = await fetchAllSuppliers()
|
||||||
|
if (supplierRes.code === 0) {
|
||||||
|
supplierOptions.value = supplierRes.details?.data || supplierRes.details || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载供应商失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有数据中心,加载楼层
|
||||||
|
if (res.details.datacenter_id) {
|
||||||
|
try {
|
||||||
|
const floorRes: any = await fetchFloorListByDatacenter(res.details.datacenter_id)
|
||||||
|
const rows = extractList(floorRes)
|
||||||
|
floorOptions.value = rows.map((floor: any) => ({
|
||||||
|
label: floor.name || floor.code || String(floor.id),
|
||||||
|
value: floor.id,
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载楼层失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有楼层,加载机柜
|
||||||
|
if (res.details.floor_id) {
|
||||||
|
try {
|
||||||
|
const rackRes: any = await fetchRackListByFloor(res.details.floor_id)
|
||||||
|
rackOptions.value = extractList(rackRes)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载机柜失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Message.error(res.message || '获取详情失败')
|
||||||
|
router.push('/ops/assets/device')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取设备详情失败:', error)
|
||||||
|
Message.error('获取详情失败')
|
||||||
|
router.push('/ops/assets/device')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回上一级
|
||||||
|
const handleBack = () => {
|
||||||
|
router.push('/assets/device')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
const valid = await formRef.value?.validate()
|
||||||
|
if (valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
submitting.value = true
|
||||||
|
|
||||||
|
const data = { ...form.value }
|
||||||
|
|
||||||
|
let res
|
||||||
|
if (isEdit.value && deviceId.value) {
|
||||||
|
res = await updateAsset({ ...data, id: deviceId.value })
|
||||||
|
} else {
|
||||||
|
res = await createAsset(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
Message.success(isEdit.value ? '更新成功' : '创建成功')
|
||||||
|
handleBack()
|
||||||
|
} else {
|
||||||
|
Message.error(res.message || '操作失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('提交表单失败:', error)
|
||||||
|
Message.error('操作失败')
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
initPage()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'DeviceForm',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.device-form-page {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--color-fill-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background-color: var(--color-bg-2);
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单项样式优化
|
||||||
|
:deep(.arco-form-item) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-card) {
|
||||||
|
.arco-card-header {
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -20,9 +20,6 @@
|
|||||||
<a-button type="primary" @click="handleCreate">
|
<a-button type="primary" @click="handleCreate">
|
||||||
新增设备
|
新增设备
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button @click="handleExport">
|
|
||||||
导出
|
|
||||||
</a-button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 序号 -->
|
<!-- 序号 -->
|
||||||
@@ -66,13 +63,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
|
|
||||||
<!-- 设备表单对话框(新增/编辑) -->
|
|
||||||
<device-form-dialog
|
|
||||||
v-model:visible="formVisible"
|
|
||||||
:device="editingDevice"
|
|
||||||
@success="handleFormSuccess"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 设备详情对话框 -->
|
<!-- 设备详情对话框 -->
|
||||||
<device-detail-dialog
|
<device-detail-dialog
|
||||||
v-model:visible="detailVisible"
|
v-model:visible="detailVisible"
|
||||||
@@ -83,6 +73,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import { Message, Modal } from '@arco-design/web-vue'
|
import { Message, Modal } from '@arco-design/web-vue'
|
||||||
import type { FormItem } from '@/components/search-form/types'
|
import type { FormItem } from '@/components/search-form/types'
|
||||||
import SearchTable from '@/components/search-table/index.vue'
|
import SearchTable from '@/components/search-table/index.vue'
|
||||||
@@ -94,8 +85,9 @@ import {
|
|||||||
getAssetStatusColor,
|
getAssetStatusColor,
|
||||||
assetStatusOptions,
|
assetStatusOptions,
|
||||||
} from '@/api/ops/asset'
|
} from '@/api/ops/asset'
|
||||||
import DeviceFormDialog from './components/DeviceFormDialog.vue'
|
import DeviceDetailDialog from '../components/DeviceDetailDialog.vue'
|
||||||
import DeviceDetailDialog from './components/DeviceDetailDialog.vue'
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
@@ -204,17 +196,15 @@ const columns = computed(() => [
|
|||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
slotName: 'actions',
|
slotName: 'actions',
|
||||||
width: 280,
|
width: 220,
|
||||||
fixed: 'right' as const,
|
fixed: 'right' as const,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
// 当前选中的设备
|
// 当前选中的设备
|
||||||
const currentDevice = ref<any>(null)
|
const currentDevice = ref<any>(null)
|
||||||
const editingDevice = ref<any>(null)
|
|
||||||
|
|
||||||
// 对话框可见性
|
// 对话框可见性
|
||||||
const formVisible = ref(false)
|
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
|
|
||||||
// 获取设备列表
|
// 获取设备列表
|
||||||
@@ -276,16 +266,17 @@ const handleRefresh = () => {
|
|||||||
Message.success('数据已刷新')
|
Message.success('数据已刷新')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增设备
|
// 新增设备 - 跳转到新增页面
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
editingDevice.value = null
|
router.push('/assets/device/form')
|
||||||
formVisible.value = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑设备
|
// 编辑设备 - 跳转到编辑页面
|
||||||
const handleEdit = (record: any) => {
|
const handleEdit = (record: any) => {
|
||||||
editingDevice.value = record
|
router.push({
|
||||||
formVisible.value = true
|
path: '/assets/device/form',
|
||||||
|
query: { id: record.id },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 详情
|
// 详情
|
||||||
@@ -335,7 +326,7 @@ const handleExport = async () => {
|
|||||||
keyword: formModel.value.keyword || undefined,
|
keyword: formModel.value.keyword || undefined,
|
||||||
status: formModel.value.status || undefined,
|
status: formModel.value.status || undefined,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (res.code === 0 && res.details?.data) {
|
if (res.code === 0 && res.details?.data) {
|
||||||
const data = res.details.data
|
const data = res.details.data
|
||||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
|
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
|
||||||
@@ -353,12 +344,6 @@ const handleExport = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表单成功回调
|
|
||||||
const handleFormSuccess = () => {
|
|
||||||
formVisible.value = false
|
|
||||||
fetchDevices()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 格式化金额
|
// 格式化金额
|
||||||
const formatMoney = (value: number | string | null | undefined) => {
|
const formatMoney = (value: number | string | null | undefined) => {
|
||||||
if (value === null || value === undefined || value === '') return '-'
|
if (value === null || value === undefined || value === '') return '-'
|
||||||
@@ -389,7 +374,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'DeviceManage',
|
name: 'DeviceList',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -1,464 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a-modal
|
|
||||||
:visible="visible"
|
|
||||||
:title="isEdit ? '编辑供应商' : '新增供应商'"
|
|
||||||
width="800px"
|
|
||||||
@ok="handleOk"
|
|
||||||
@cancel="handleCancel"
|
|
||||||
@update:visible="handleVisibleChange"
|
|
||||||
:confirm-loading="submitting"
|
|
||||||
>
|
|
||||||
<a-form :model="form" layout="vertical" ref="formRef">
|
|
||||||
<a-tabs v-model:active-key="activeTab">
|
|
||||||
<!-- 基本信息 -->
|
|
||||||
<a-tab-pane key="basic" title="基本信息">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="供应商名称"
|
|
||||||
field="name"
|
|
||||||
:rules="[{ required: true, message: '请输入供应商名称' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.name"
|
|
||||||
placeholder="请输入供应商名称"
|
|
||||||
:max-length="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="供应商编码"
|
|
||||||
field="code"
|
|
||||||
:rules="[{ required: true, message: '请输入供应商编码' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.code"
|
|
||||||
placeholder="请输入供应商编码"
|
|
||||||
:max-length="100"
|
|
||||||
:disabled="isEdit"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="简称" field="short_name">
|
|
||||||
<a-input
|
|
||||||
v-model="form.short_name"
|
|
||||||
placeholder="请输入简称"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="供应商类型"
|
|
||||||
field="supplier_type"
|
|
||||||
:rules="[{ required: true, message: '请选择供应商类型' }]"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model="form.supplier_type"
|
|
||||||
placeholder="请选择供应商类型"
|
|
||||||
allow-clear
|
|
||||||
>
|
|
||||||
<a-option value="生产商">生产商</a-option>
|
|
||||||
<a-option value="代理商">代理商</a-option>
|
|
||||||
<a-option value="经销商">经销商</a-option>
|
|
||||||
<a-option value="服务商">服务商</a-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="供应商级别" field="supplier_level">
|
|
||||||
<a-select
|
|
||||||
v-model="form.supplier_level"
|
|
||||||
placeholder="请选择供应商级别"
|
|
||||||
allow-clear
|
|
||||||
>
|
|
||||||
<a-option value="A">A</a-option>
|
|
||||||
<a-option value="B">B</a-option>
|
|
||||||
<a-option value="C">C</a-option>
|
|
||||||
<a-option value="D">D</a-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="状态"
|
|
||||||
field="status"
|
|
||||||
:rules="[{ required: true, message: '请选择状态' }]"
|
|
||||||
>
|
|
||||||
<a-select
|
|
||||||
v-model="form.status"
|
|
||||||
placeholder="请选择状态"
|
|
||||||
>
|
|
||||||
<a-option value="active">合作中</a-option>
|
|
||||||
<a-option value="paused">暂停合作</a-option>
|
|
||||||
<a-option value="blacklist">黑名单</a-option>
|
|
||||||
<a-option value="inactive">停止合作</a-option>
|
|
||||||
</a-select>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<!-- 联系信息 -->
|
|
||||||
<a-tab-pane key="contact" title="联系信息">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="联系人"
|
|
||||||
field="contact_person"
|
|
||||||
:rules="[{ required: true, message: '请输入联系人' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.contact_person"
|
|
||||||
placeholder="请输入联系人"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="联系电话"
|
|
||||||
field="contact_phone"
|
|
||||||
:rules="[{ required: true, message: '请输入联系电话' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.contact_phone"
|
|
||||||
placeholder="请输入联系电话"
|
|
||||||
:max-length="20"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="备用联系电话"
|
|
||||||
field="contact_mobile"
|
|
||||||
:rules="[{ required: true, message: '请输入备用联系电话' }]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.contact_mobile"
|
|
||||||
placeholder="请输入备用联系电话"
|
|
||||||
:max-length="20"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item
|
|
||||||
label="联系邮箱"
|
|
||||||
field="contact_email"
|
|
||||||
:rules="[
|
|
||||||
{ required: true, message: '请输入联系邮箱' },
|
|
||||||
{ type: 'email', message: '请输入有效的邮箱地址' },
|
|
||||||
]"
|
|
||||||
>
|
|
||||||
<a-input
|
|
||||||
v-model="form.contact_email"
|
|
||||||
placeholder="请输入联系邮箱"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<!-- 地址信息 -->
|
|
||||||
<a-tab-pane key="address" title="地址信息">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="省份" field="province">
|
|
||||||
<a-input
|
|
||||||
v-model="form.province"
|
|
||||||
placeholder="请输入省份"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="城市" field="city">
|
|
||||||
<a-input
|
|
||||||
v-model="form.city"
|
|
||||||
placeholder="请输入城市"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="邮编" field="postal_code">
|
|
||||||
<a-input
|
|
||||||
v-model="form.postal_code"
|
|
||||||
placeholder="请输入邮编"
|
|
||||||
:max-length="10"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="详细地址" field="address">
|
|
||||||
<a-input
|
|
||||||
v-model="form.address"
|
|
||||||
placeholder="请输入详细地址"
|
|
||||||
:max-length="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</a-tab-pane>
|
|
||||||
|
|
||||||
<!-- 企业信息 -->
|
|
||||||
<a-tab-pane key="company" title="企业信息">
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="法定代表人" field="legal_representative">
|
|
||||||
<a-input
|
|
||||||
v-model="form.legal_representative"
|
|
||||||
placeholder="请输入法定代表人"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="注册资本" field="registered_capital">
|
|
||||||
<a-input
|
|
||||||
v-model="form.registered_capital"
|
|
||||||
placeholder="请输入注册资本"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="营业执照号" field="business_license">
|
|
||||||
<a-input
|
|
||||||
v-model="form.business_license"
|
|
||||||
placeholder="请输入营业执照号"
|
|
||||||
:max-length="100"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="税号" field="tax_id">
|
|
||||||
<a-input
|
|
||||||
v-model="form.tax_id"
|
|
||||||
placeholder="请输入税号"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-row :gutter="16">
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="企业类型" field="company_type">
|
|
||||||
<a-input
|
|
||||||
v-model="form.company_type"
|
|
||||||
placeholder="请输入企业类型"
|
|
||||||
:max-length="50"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<a-form-item label="官网地址" field="website">
|
|
||||||
<a-input
|
|
||||||
v-model="form.website"
|
|
||||||
placeholder="请输入官网地址"
|
|
||||||
:max-length="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
|
|
||||||
<a-form-item label="供应商描述" field="description">
|
|
||||||
<a-textarea
|
|
||||||
v-model="form.description"
|
|
||||||
placeholder="请输入供应商描述"
|
|
||||||
:auto-size="{ minRows: 3, maxRows: 6 }"
|
|
||||||
:max-length="500"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
</a-tab-pane>
|
|
||||||
</a-tabs>
|
|
||||||
</a-form>
|
|
||||||
</a-modal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, watch, computed } from 'vue'
|
|
||||||
import { Message } from '@arco-design/web-vue'
|
|
||||||
import { createSupplier, updateSupplier } from '@/api/ops/supplier'
|
|
||||||
|
|
||||||
interface Supplier {
|
|
||||||
id?: number
|
|
||||||
name?: string
|
|
||||||
code?: string
|
|
||||||
short_name?: string
|
|
||||||
description?: string
|
|
||||||
contact_person?: string
|
|
||||||
contact_phone?: string
|
|
||||||
contact_email?: string
|
|
||||||
contact_mobile?: string
|
|
||||||
province?: string
|
|
||||||
city?: string
|
|
||||||
address?: string
|
|
||||||
postal_code?: string
|
|
||||||
website?: string
|
|
||||||
legal_representative?: string
|
|
||||||
registered_capital?: string
|
|
||||||
business_license?: string
|
|
||||||
tax_id?: string
|
|
||||||
company_type?: string
|
|
||||||
supplier_type?: string
|
|
||||||
supplier_level?: string
|
|
||||||
status?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
visible: boolean
|
|
||||||
supplier: Supplier | null
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Emits {
|
|
||||||
(e: 'update:visible', value: boolean): void
|
|
||||||
(e: 'success'): void
|
|
||||||
}
|
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
|
||||||
const emit = defineEmits<Emits>()
|
|
||||||
|
|
||||||
const formRef = ref()
|
|
||||||
const submitting = ref(false)
|
|
||||||
const activeTab = ref('basic')
|
|
||||||
|
|
||||||
// 是否为编辑模式
|
|
||||||
const isEdit = computed(() => !!props.supplier?.id)
|
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const form = ref<Supplier>({
|
|
||||||
name: '',
|
|
||||||
code: '',
|
|
||||||
short_name: '',
|
|
||||||
description: '',
|
|
||||||
contact_person: '',
|
|
||||||
contact_phone: '',
|
|
||||||
contact_email: '',
|
|
||||||
contact_mobile: '',
|
|
||||||
province: '',
|
|
||||||
city: '',
|
|
||||||
address: '',
|
|
||||||
postal_code: '',
|
|
||||||
website: '',
|
|
||||||
legal_representative: '',
|
|
||||||
registered_capital: '',
|
|
||||||
business_license: '',
|
|
||||||
tax_id: '',
|
|
||||||
company_type: '',
|
|
||||||
supplier_type: '',
|
|
||||||
supplier_level: '',
|
|
||||||
status: 'active',
|
|
||||||
})
|
|
||||||
|
|
||||||
// 监听 supplier 变化,初始化表单
|
|
||||||
watch(
|
|
||||||
() => props.supplier,
|
|
||||||
(newVal) => {
|
|
||||||
if (newVal) {
|
|
||||||
form.value = { ...newVal }
|
|
||||||
} else {
|
|
||||||
// 重置表单
|
|
||||||
form.value = {
|
|
||||||
name: '',
|
|
||||||
code: '',
|
|
||||||
short_name: '',
|
|
||||||
description: '',
|
|
||||||
contact_person: '',
|
|
||||||
contact_phone: '',
|
|
||||||
contact_email: '',
|
|
||||||
contact_mobile: '',
|
|
||||||
province: '',
|
|
||||||
city: '',
|
|
||||||
address: '',
|
|
||||||
postal_code: '',
|
|
||||||
website: '',
|
|
||||||
legal_representative: '',
|
|
||||||
registered_capital: '',
|
|
||||||
business_license: '',
|
|
||||||
tax_id: '',
|
|
||||||
company_type: '',
|
|
||||||
supplier_type: '',
|
|
||||||
supplier_level: '',
|
|
||||||
status: 'active',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
activeTab.value = 'basic'
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// 提交表单
|
|
||||||
const handleOk = async () => {
|
|
||||||
try {
|
|
||||||
const valid = await formRef.value?.validate()
|
|
||||||
if (valid) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
submitting.value = true
|
|
||||||
|
|
||||||
const data = { ...form.value }
|
|
||||||
|
|
||||||
let res
|
|
||||||
if (isEdit.value) {
|
|
||||||
res = await updateSupplier({ ...data, id: props.supplier?.id })
|
|
||||||
} else {
|
|
||||||
res = await createSupplier(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.code === 0) {
|
|
||||||
Message.success(isEdit.value ? '更新成功' : '创建成功')
|
|
||||||
emit('success')
|
|
||||||
} else {
|
|
||||||
Message.error(res.message || '操作失败')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('提交表单失败:', error)
|
|
||||||
Message.error('操作失败')
|
|
||||||
} finally {
|
|
||||||
submitting.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消
|
|
||||||
const handleCancel = () => {
|
|
||||||
emit('update:visible', false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 处理对话框可见性变化
|
|
||||||
const handleVisibleChange = (visible: boolean) => {
|
|
||||||
emit('update:visible', visible)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
name: 'SupplierFormDialog',
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
:deep(.arco-tabs-content) {
|
|
||||||
padding-top: 16px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
490
src/views/ops/pages/assets/supplier/form/index.vue
Normal file
490
src/views/ops/pages/assets/supplier/form/index.vue
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
<template>
|
||||||
|
<div class="supplier-form-page">
|
||||||
|
<!-- 页面头部 -->
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-left">
|
||||||
|
<a-button type="text" @click="handleBack">
|
||||||
|
<template #icon><icon-left /></template>
|
||||||
|
返回列表
|
||||||
|
</a-button>
|
||||||
|
<a-divider direction="vertical" />
|
||||||
|
<h2 class="page-title">{{ pageTitle }}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="header-right">
|
||||||
|
<a-space>
|
||||||
|
<a-button @click="handleBack">取消</a-button>
|
||||||
|
<a-button type="primary" :loading="submitting" @click="handleSubmit">
|
||||||
|
{{ isEdit ? '保存' : '创建' }}
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 表单内容 -->
|
||||||
|
<div class="page-content">
|
||||||
|
<a-spin :loading="loading" style="width: 100%">
|
||||||
|
<a-form ref="formRef" :model="form" layout="vertical">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<a-card class="info-card" title="基本信息">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="供应商名称" field="name" required>
|
||||||
|
<a-input
|
||||||
|
v-model="form.name"
|
||||||
|
placeholder="请输入供应商名称"
|
||||||
|
:max-length="200"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="供应商编码" field="code" required>
|
||||||
|
<a-input
|
||||||
|
v-model="form.code"
|
||||||
|
placeholder="请输入供应商编码"
|
||||||
|
:max-length="100"
|
||||||
|
:disabled="isEdit"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="简称" field="short_name">
|
||||||
|
<a-input
|
||||||
|
v-model="form.short_name"
|
||||||
|
placeholder="请输入简称"
|
||||||
|
:max-length="100"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="供应商类型" field="supplier_type" required>
|
||||||
|
<a-select
|
||||||
|
v-model="form.supplier_type"
|
||||||
|
placeholder="请选择供应商类型"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<a-option value="生产商">生产商</a-option>
|
||||||
|
<a-option value="代理商">代理商</a-option>
|
||||||
|
<a-option value="经销商">经销商</a-option>
|
||||||
|
<a-option value="服务商">服务商</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="供应商级别" field="supplier_level">
|
||||||
|
<a-select
|
||||||
|
v-model="form.supplier_level"
|
||||||
|
placeholder="请选择供应商级别"
|
||||||
|
allow-clear
|
||||||
|
>
|
||||||
|
<a-option value="A">A</a-option>
|
||||||
|
<a-option value="B">B</a-option>
|
||||||
|
<a-option value="C">C</a-option>
|
||||||
|
<a-option value="D">D</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="状态" field="status" required>
|
||||||
|
<a-select v-model="form.status" placeholder="请选择状态">
|
||||||
|
<a-option value="active">合作中</a-option>
|
||||||
|
<a-option value="paused">暂停合作</a-option>
|
||||||
|
<a-option value="blacklist">黑名单</a-option>
|
||||||
|
<a-option value="inactive">停止合作</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 联系信息 -->
|
||||||
|
<a-card class="info-card" title="联系信息">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="联系人" field="contact_person" required>
|
||||||
|
<a-input
|
||||||
|
v-model="form.contact_person"
|
||||||
|
placeholder="请输入联系人"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="联系电话" field="contact_phone" required>
|
||||||
|
<a-input
|
||||||
|
v-model="form.contact_phone"
|
||||||
|
placeholder="请输入联系电话"
|
||||||
|
:max-length="20"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="备用联系电话" field="contact_mobile" required>
|
||||||
|
<a-input
|
||||||
|
v-model="form.contact_mobile"
|
||||||
|
placeholder="请输入备用联系电话"
|
||||||
|
:max-length="20"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="联系邮箱" field="contact_email" required>
|
||||||
|
<a-input
|
||||||
|
v-model="form.contact_email"
|
||||||
|
placeholder="请输入联系邮箱"
|
||||||
|
:max-length="100"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 地址信息 -->
|
||||||
|
<a-card class="info-card" title="地址信息">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="省份" field="province">
|
||||||
|
<a-input
|
||||||
|
v-model="form.province"
|
||||||
|
placeholder="请输入省份"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="城市" field="city">
|
||||||
|
<a-input
|
||||||
|
v-model="form.city"
|
||||||
|
placeholder="请输入城市"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="邮编" field="postal_code">
|
||||||
|
<a-input
|
||||||
|
v-model="form.postal_code"
|
||||||
|
placeholder="请输入邮编"
|
||||||
|
:max-length="10"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="详细地址" field="address">
|
||||||
|
<a-input
|
||||||
|
v-model="form.address"
|
||||||
|
placeholder="请输入详细地址"
|
||||||
|
:max-length="200"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 企业信息 -->
|
||||||
|
<a-card class="info-card" title="企业信息">
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="法定代表人" field="legal_representative">
|
||||||
|
<a-input
|
||||||
|
v-model="form.legal_representative"
|
||||||
|
placeholder="请输入法定代表人"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="注册资本" field="registered_capital">
|
||||||
|
<a-input
|
||||||
|
v-model="form.registered_capital"
|
||||||
|
placeholder="请输入注册资本"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="营业执照号" field="business_license">
|
||||||
|
<a-input
|
||||||
|
v-model="form.business_license"
|
||||||
|
placeholder="请输入营业执照号"
|
||||||
|
:max-length="100"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="税号" field="tax_id">
|
||||||
|
<a-input
|
||||||
|
v-model="form.tax_id"
|
||||||
|
placeholder="请输入税号"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="企业类型" field="company_type">
|
||||||
|
<a-input
|
||||||
|
v-model="form.company_type"
|
||||||
|
placeholder="请输入企业类型"
|
||||||
|
:max-length="50"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="6">
|
||||||
|
<a-form-item label="官网地址" field="website">
|
||||||
|
<a-input
|
||||||
|
v-model="form.website"
|
||||||
|
placeholder="请输入官网地址"
|
||||||
|
:max-length="200"
|
||||||
|
allow-clear
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="供应商描述" field="description">
|
||||||
|
<a-textarea
|
||||||
|
v-model="form.description"
|
||||||
|
placeholder="请输入供应商描述"
|
||||||
|
:auto-size="{ minRows: 3, maxRows: 6 }"
|
||||||
|
:max-length="500"
|
||||||
|
show-word-limit
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</a-form>
|
||||||
|
</a-spin>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import type { FormInstance } from '@arco-design/web-vue/es/form'
|
||||||
|
import {
|
||||||
|
createSupplier,
|
||||||
|
updateSupplier,
|
||||||
|
fetchSupplierDetail,
|
||||||
|
} from '@/api/ops/supplier'
|
||||||
|
|
||||||
|
interface Supplier {
|
||||||
|
id?: number
|
||||||
|
name?: string
|
||||||
|
code?: string
|
||||||
|
short_name?: string
|
||||||
|
description?: string
|
||||||
|
contact_person?: string
|
||||||
|
contact_phone?: string
|
||||||
|
contact_email?: string
|
||||||
|
contact_mobile?: string
|
||||||
|
province?: string
|
||||||
|
city?: string
|
||||||
|
address?: string
|
||||||
|
postal_code?: string
|
||||||
|
website?: string
|
||||||
|
legal_representative?: string
|
||||||
|
registered_capital?: string
|
||||||
|
business_license?: string
|
||||||
|
tax_id?: string
|
||||||
|
company_type?: string
|
||||||
|
supplier_type?: string
|
||||||
|
supplier_level?: string
|
||||||
|
status?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
// 页面状态
|
||||||
|
const isEdit = ref(false)
|
||||||
|
const supplierId = ref<number | null>(null)
|
||||||
|
const pageTitle = ref('新增供应商')
|
||||||
|
const loading = ref(false)
|
||||||
|
const submitting = ref(false)
|
||||||
|
const formRef = ref<FormInstance>()
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const form = ref<Supplier>({
|
||||||
|
name: '',
|
||||||
|
code: '',
|
||||||
|
short_name: '',
|
||||||
|
description: '',
|
||||||
|
contact_person: '',
|
||||||
|
contact_phone: '',
|
||||||
|
contact_email: '',
|
||||||
|
contact_mobile: '',
|
||||||
|
province: '',
|
||||||
|
city: '',
|
||||||
|
address: '',
|
||||||
|
postal_code: '',
|
||||||
|
website: '',
|
||||||
|
legal_representative: '',
|
||||||
|
registered_capital: '',
|
||||||
|
business_license: '',
|
||||||
|
tax_id: '',
|
||||||
|
company_type: '',
|
||||||
|
supplier_type: '',
|
||||||
|
supplier_level: '',
|
||||||
|
status: 'active',
|
||||||
|
})
|
||||||
|
|
||||||
|
// 初始化页面
|
||||||
|
const initPage = async () => {
|
||||||
|
const id = route.query.id
|
||||||
|
if (id) {
|
||||||
|
isEdit.value = true
|
||||||
|
supplierId.value = Number(id)
|
||||||
|
pageTitle.value = '编辑供应商'
|
||||||
|
await loadSupplierDetail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载供应商详情
|
||||||
|
const loadSupplierDetail = async () => {
|
||||||
|
if (!supplierId.value) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await fetchSupplierDetail(supplierId.value)
|
||||||
|
if (res.code === 0) {
|
||||||
|
form.value = { ...res.details }
|
||||||
|
} else {
|
||||||
|
Message.error(res.message || '获取详情失败')
|
||||||
|
router.push('/assets/supplier')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取供应商详情失败:', error)
|
||||||
|
Message.error('获取详情失败')
|
||||||
|
router.push('/assets/supplier')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回上一级
|
||||||
|
const handleBack = () => {
|
||||||
|
router.push('/assets/supplier')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
const valid = await formRef.value?.validate()
|
||||||
|
if (valid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
submitting.value = true
|
||||||
|
|
||||||
|
const data = { ...form.value }
|
||||||
|
|
||||||
|
let res
|
||||||
|
if (isEdit.value && supplierId.value) {
|
||||||
|
res = await updateSupplier({ ...data, id: supplierId.value })
|
||||||
|
} else {
|
||||||
|
res = await createSupplier(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res.code === 0) {
|
||||||
|
Message.success(isEdit.value ? '更新成功' : '创建成功')
|
||||||
|
handleBack()
|
||||||
|
} else {
|
||||||
|
Message.error(res.message || '操作失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('提交表单失败:', error)
|
||||||
|
Message.error('操作失败')
|
||||||
|
} finally {
|
||||||
|
submitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
initPage()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: 'SupplierForm',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="less">
|
||||||
|
.supplier-form-page {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: var(--color-fill-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background-color: var(--color-bg-2);
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单项样式优化
|
||||||
|
:deep(.arco-form-item) {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-card) {
|
||||||
|
.arco-card-header {
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -58,13 +58,6 @@
|
|||||||
</template>
|
</template>
|
||||||
</search-table>
|
</search-table>
|
||||||
|
|
||||||
<!-- 供应商表单对话框(新增/编辑) -->
|
|
||||||
<supplier-form-dialog
|
|
||||||
v-model:visible="formVisible"
|
|
||||||
:supplier="editingSupplier"
|
|
||||||
@success="handleFormSuccess"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 供应商详情对话框 -->
|
<!-- 供应商详情对话框 -->
|
||||||
<supplier-detail-dialog
|
<supplier-detail-dialog
|
||||||
v-model:visible="detailVisible"
|
v-model:visible="detailVisible"
|
||||||
@@ -75,6 +68,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import { Message, Modal } from '@arco-design/web-vue'
|
import { Message, Modal } from '@arco-design/web-vue'
|
||||||
import type { FormItem } from '@/components/search-form/types'
|
import type { FormItem } from '@/components/search-form/types'
|
||||||
import SearchTable from '@/components/search-table/index.vue'
|
import SearchTable from '@/components/search-table/index.vue'
|
||||||
@@ -83,8 +77,9 @@ import {
|
|||||||
deleteSupplier,
|
deleteSupplier,
|
||||||
fetchSupplierDetail,
|
fetchSupplierDetail,
|
||||||
} from '@/api/ops/supplier'
|
} from '@/api/ops/supplier'
|
||||||
import SupplierFormDialog from './components/SupplierFormDialog.vue'
|
import SupplierDetailDialog from '../components/SupplierDetailDialog.vue'
|
||||||
import SupplierDetailDialog from './components/SupplierDetailDialog.vue'
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
// 状态选项
|
// 状态选项
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
@@ -200,10 +195,8 @@ const columns = computed(() => [
|
|||||||
|
|
||||||
// 当前选中的供应商
|
// 当前选中的供应商
|
||||||
const currentSupplier = ref<any>(null)
|
const currentSupplier = ref<any>(null)
|
||||||
const editingSupplier = ref<any>(null)
|
|
||||||
|
|
||||||
// 对话框可见性
|
// 对话框可见性
|
||||||
const formVisible = ref(false)
|
|
||||||
const detailVisible = ref(false)
|
const detailVisible = ref(false)
|
||||||
|
|
||||||
// 获取供应商列表
|
// 获取供应商列表
|
||||||
@@ -265,16 +258,17 @@ const handleRefresh = () => {
|
|||||||
Message.success('数据已刷新')
|
Message.success('数据已刷新')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增供应商
|
// 新增供应商 - 跳转到新增页面
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
editingSupplier.value = null
|
router.push('/assets/supplier/form')
|
||||||
formVisible.value = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 编辑供应商
|
// 编辑供应商 - 跳转到编辑页面(通过 query 参数传递 id)
|
||||||
const handleEdit = (record: any) => {
|
const handleEdit = (record: any) => {
|
||||||
editingSupplier.value = record
|
router.push({
|
||||||
formVisible.value = true
|
path: '/assets/supplier/form',
|
||||||
|
query: { id: record.id },
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 详情
|
// 详情
|
||||||
@@ -314,12 +308,6 @@ const handleDelete = async (record: any) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表单成功回调
|
|
||||||
const handleFormSuccess = () => {
|
|
||||||
formVisible.value = false
|
|
||||||
fetchSuppliers()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取状态颜色
|
// 获取状态颜色
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
const colorMap: Record<string, string> = {
|
const colorMap: Record<string, string> = {
|
||||||
@@ -373,7 +361,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'SupplierManage',
|
name: 'SupplierList',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -146,7 +146,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, reactive, computed, onMounted } from 'vue'
|
import { ref, reactive, computed, onMounted } from 'vue'
|
||||||
import { Message } from '@arco-design/web-vue'
|
import { Message, Modal } from '@arco-design/web-vue'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import SearchTable from '@/components/search-table/index.vue'
|
import SearchTable from '@/components/search-table/index.vue'
|
||||||
import type { FormItem } from '@/components/search-form/types'
|
import type { FormItem } from '@/components/search-form/types'
|
||||||
@@ -378,25 +378,36 @@ const handleView = (record: ReviewListItem) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 审核通过
|
// 审核通过
|
||||||
const handleApprove = async (record: ReviewListItem) => {
|
const handleApprove = (record: ReviewListItem) => {
|
||||||
try {
|
const resourceTitle = getResourceTitle(record)
|
||||||
loading.value = true
|
const resourceTypeText = getResourceTypeText(record.type)
|
||||||
const res: any = await approveReview({
|
|
||||||
resource_type: record.type,
|
Modal.confirm({
|
||||||
id: record.resource.id,
|
title: '确认审核通过',
|
||||||
})
|
content: `确定要通过该${resourceTypeText}的审核吗?\n标题:${resourceTitle}`,
|
||||||
if (res?.code === 0) {
|
okText: '确认通过',
|
||||||
Message.success('审核通过')
|
cancelText: '取消',
|
||||||
await fetchData()
|
onOk: async () => {
|
||||||
} else {
|
try {
|
||||||
Message.error(res?.message || '审核失败')
|
loading.value = true
|
||||||
}
|
const res: any = await approveReview({
|
||||||
} catch (error) {
|
resource_type: record.type,
|
||||||
console.error('审核失败:', error)
|
id: record.resource.id,
|
||||||
Message.error('审核失败')
|
})
|
||||||
} finally {
|
if (res?.code === 0) {
|
||||||
loading.value = false
|
Message.success('审核通过')
|
||||||
}
|
await fetchData()
|
||||||
|
} else {
|
||||||
|
Message.error(res?.message || '审核失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('审核失败:', error)
|
||||||
|
Message.error('审核失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 拒绝
|
// 拒绝
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ const handleOk = async () => {
|
|||||||
res = await createCategory(data)
|
res = await createCategory(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.code === 200) {
|
if (res.code === 0) {
|
||||||
Message.success(isEdit.value ? '编辑成功' : '创建成功')
|
Message.success(isEdit.value ? '编辑成功' : '创建成功')
|
||||||
emit('success')
|
emit('success')
|
||||||
emit('update:visible', false)
|
emit('update:visible', false)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
:data="tableData"
|
:data="tableData"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
title="分类管理"
|
title="分类管理"
|
||||||
search-button-text="查询"
|
search-button-text="查询"
|
||||||
reset-button-text="重置"
|
reset-button-text="重置"
|
||||||
@@ -13,6 +14,8 @@
|
|||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
@reset="handleReset"
|
@reset="handleReset"
|
||||||
@refresh="handleRefresh"
|
@refresh="handleRefresh"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
>
|
>
|
||||||
<template #toolbar-left>
|
<template #toolbar-left>
|
||||||
<a-button type="primary" @click="handleCreate">
|
<a-button type="primary" @click="handleCreate">
|
||||||
@@ -66,12 +69,20 @@ import CategoryFormDialog from './components/CategoryFormDialog.vue'
|
|||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const tableData = ref<Category[]>([])
|
const allData = ref<Category[]>([]) // 存储全量数据
|
||||||
|
const tableData = ref<Category[]>([]) // 当前页数据
|
||||||
const formModel = ref({
|
const formModel = ref({
|
||||||
keyword: '',
|
keyword: '',
|
||||||
type: '',
|
type: '',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 分页状态
|
||||||
|
const pagination = ref({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
total: 0,
|
||||||
|
})
|
||||||
|
|
||||||
// 表单项配置
|
// 表单项配置
|
||||||
const formItems = computed<FormItem[]>(() => [
|
const formItems = computed<FormItem[]>(() => [
|
||||||
{
|
{
|
||||||
@@ -122,6 +133,12 @@ const columns = computed(() => [
|
|||||||
width: 120,
|
width: 120,
|
||||||
align: 'center' as const,
|
align: 'center' as const,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '排序',
|
||||||
|
dataIndex: 'sort_order',
|
||||||
|
width: 80,
|
||||||
|
align: 'center' as const,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '备注信息',
|
title: '备注信息',
|
||||||
dataIndex: 'remarks',
|
dataIndex: 'remarks',
|
||||||
@@ -162,6 +179,14 @@ const getTypeColor = (type: string) => {
|
|||||||
return colorMap[type] || 'gray'
|
return colorMap[type] || 'gray'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新表格数据(前端分页)
|
||||||
|
const updateTableData = () => {
|
||||||
|
const { current, pageSize } = pagination.value
|
||||||
|
const start = (current - 1) * pageSize
|
||||||
|
const end = start + pageSize
|
||||||
|
tableData.value = allData.value.slice(start, end)
|
||||||
|
}
|
||||||
|
|
||||||
// 获取分类列表
|
// 获取分类列表
|
||||||
const fetchCategories = async () => {
|
const fetchCategories = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -180,20 +205,29 @@ const fetchCategories = async () => {
|
|||||||
|
|
||||||
// 如果有关键词搜索,进行过滤
|
// 如果有关键词搜索,进行过滤
|
||||||
if (formModel.value.keyword) {
|
if (formModel.value.keyword) {
|
||||||
data = data.filter((item: Category) =>
|
data = data.filter((item: Category) =>
|
||||||
item.name.toLowerCase().includes(formModel.value.keyword.toLowerCase())
|
item.name.toLowerCase().includes(formModel.value.keyword.toLowerCase())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
tableData.value = data
|
// 保存全量数据并更新分页
|
||||||
|
allData.value = data
|
||||||
|
pagination.value.total = data.length
|
||||||
|
// 重置到第一页
|
||||||
|
pagination.value.current = 1
|
||||||
|
updateTableData()
|
||||||
} else {
|
} else {
|
||||||
Message.error(res.message || '获取分类列表失败')
|
Message.error(res.message || '获取分类列表失败')
|
||||||
|
allData.value = []
|
||||||
tableData.value = []
|
tableData.value = []
|
||||||
|
pagination.value.total = 0
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取分类列表失败:', error)
|
console.error('获取分类列表失败:', error)
|
||||||
Message.error('获取分类列表失败')
|
Message.error('获取分类列表失败')
|
||||||
|
allData.value = []
|
||||||
tableData.value = []
|
tableData.value = []
|
||||||
|
pagination.value.total = 0
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -224,6 +258,19 @@ const handleRefresh = () => {
|
|||||||
Message.success('数据已刷新')
|
Message.success('数据已刷新')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分页切换
|
||||||
|
const handlePageChange = (current: number) => {
|
||||||
|
pagination.value.current = current
|
||||||
|
updateTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 每页条数切换
|
||||||
|
const handlePageSizeChange = (pageSize: number) => {
|
||||||
|
pagination.value.current = 1
|
||||||
|
pagination.value.pageSize = pageSize
|
||||||
|
updateTableData()
|
||||||
|
}
|
||||||
|
|
||||||
// 新增分类
|
// 新增分类
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
editingCategory.value = null
|
editingCategory.value = null
|
||||||
@@ -244,7 +291,7 @@ const handleDelete = async (record: Category) => {
|
|||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
try {
|
try {
|
||||||
const res: any = await deleteCategory(record.id)
|
const res: any = await deleteCategory(record.id)
|
||||||
if (res.code === 200) {
|
if (res.code === 0) {
|
||||||
Message.success('删除成功')
|
Message.success('删除成功')
|
||||||
fetchCategories()
|
fetchCategories()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user