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?: {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
keyword?: string;
|
||||
parent_id?: number;
|
||||
}) => {
|
||||
return request.post("/Assets/v1/category/list", data || {});
|
||||
};
|
||||
export const fetchCategoryList = (params?: CategoryListParams) => {
|
||||
return request.post('/Assets/v1/category/list', params || {})
|
||||
}
|
||||
|
||||
/** 获取资产分类详情 */
|
||||
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) => {
|
||||
return request.post("/Assets/v1/category/create", data);
|
||||
};
|
||||
export const createCategory = (data: CategoryFormData) => {
|
||||
return request.post('/Assets/v1/category/create', data)
|
||||
}
|
||||
|
||||
/** 更新资产分类 */
|
||||
export const updateCategory = (data: any) => {
|
||||
return request.put("/Assets/v1/category/update", data);
|
||||
};
|
||||
export const updateCategory = (data: CategoryFormData) => {
|
||||
return request.put('/Assets/v1/category/update', data)
|
||||
}
|
||||
|
||||
/** 删除资产分类 */
|
||||
export const deleteCategory = (id: number) => {
|
||||
return request.delete(`/Assets/v1/category/delete/${id}`);
|
||||
};
|
||||
return request.delete(`/Assets/v1/category/delete/${id}`)
|
||||
}
|
||||
|
||||
/** 获取所有资产分类(用于下拉选择) */
|
||||
export const fetchAllCategories = () => {
|
||||
return request.get("/Assets/v1/category/all");
|
||||
};
|
||||
export const fetchAllCategories = (keyword?: string) => {
|
||||
return request.get('/Assets/v1/category/all', { params: { keyword } })
|
||||
}
|
||||
|
||||
/** 获取资产分类树形结构 */
|
||||
export const fetchCategoryTree = () => {
|
||||
return request.get("/Assets/v1/category/tree");
|
||||
};
|
||||
export const fetchCategoryTree = (keyword?: string) => {
|
||||
return request.get('/Assets/v1/category/tree', { params: { keyword } })
|
||||
}
|
||||
|
||||
/** 获取指定分类的子分类列表 */
|
||||
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 = {};
|
||||
if (keyword) params.keyword = keyword;
|
||||
if (status) params.status = status;
|
||||
if (pageSize) params.page_size = pageSize;
|
||||
return request.get("/Assets/v1/supplier/all", { params });
|
||||
};
|
||||
@@ -96,7 +96,7 @@ const OPS: AppRouteRecordRaw = {
|
||||
// {
|
||||
// path: 'assets/device',
|
||||
// name: 'AssetsDevice',
|
||||
// component: () => import('@/views/ops/pages/assets/device/index.vue'),
|
||||
// component: () => import('@/views/ops/pages/assets/device/list/index.vue'),
|
||||
// meta: {
|
||||
// locale: '设备管理',
|
||||
// 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',
|
||||
// name: 'AssetsSupplier',
|
||||
// component: () => import('@/views/ops/pages/assets/supplier/index.vue'),
|
||||
// component: () => import('@/views/ops/pages/assets/supplier/list/index.vue'),
|
||||
// meta: {
|
||||
// locale: '供应商管理',
|
||||
// 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',
|
||||
// name: 'AssetsClassify',
|
||||
// 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">
|
||||
{{ $t('login.form.rememberPassword') }}
|
||||
</a-checkbox>
|
||||
<a-link>{{ $t('login.form.forgetPassword') }}</a-link>
|
||||
</div>
|
||||
<a-button type="primary" html-type="submit" long :loading="loading">
|
||||
{{ $t('login.form.login') }}
|
||||
|
||||
@@ -1,165 +1,181 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
v-model:visible="dialogVisible"
|
||||
title="分类详情"
|
||||
width="600px"
|
||||
@cancel="handleCancel"
|
||||
@update:visible="handleVisibleChange"
|
||||
:width="600"
|
||||
:footer="false"
|
||||
unmount-on-close
|
||||
>
|
||||
<a-descriptions :data="detailData" layout="vertical" bordered :column="2" v-if="category">
|
||||
<a-descriptions-item label="分类名称" :span="1">
|
||||
{{ category.name }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="分类编码" :span="1">
|
||||
{{ category.code }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="父级分类" :span="1">
|
||||
{{ category.parent?.name || '无(一级分类)' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="层级" :span="1">
|
||||
{{ category.level }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="排序" :span="1">
|
||||
{{ category.sort }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="是否启用" :span="1">
|
||||
<a-tag :color="category.enabled ? 'green' : 'red'">
|
||||
{{ category.enabled ? '是' : '否' }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="颜色标识" :span="2">
|
||||
<div class="color-display">
|
||||
<div
|
||||
class="color-block"
|
||||
:style="{ backgroundColor: category.color || '#ccc' }"
|
||||
></div>
|
||||
<span>{{ category.color || '-' }}</span>
|
||||
</div>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="图标路径" :span="2">
|
||||
{{ category.icon || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="描述" :span="2">
|
||||
{{ category.description || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="备注" :span="2">
|
||||
{{ category.remarks || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间" :span="1">
|
||||
{{ formatDate(category.created_at) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="更新时间" :span="1">
|
||||
{{ formatDate(category.updated_at) }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建人" :span="1">
|
||||
{{ category.created_by || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="更新人" :span="1">
|
||||
{{ category.updated_by || '-' }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
<a-empty v-else description="暂无数据" />
|
||||
<a-spin :loading="loading" style="width: 100%">
|
||||
<a-descriptions
|
||||
v-if="categoryDetail"
|
||||
:column="2"
|
||||
bordered
|
||||
size="medium"
|
||||
>
|
||||
<a-descriptions-item label="分类名称">
|
||||
{{ categoryDetail.name }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="分类编码">
|
||||
{{ categoryDetail.code }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="层级">
|
||||
<a-tag :color="getLevelColor(categoryDetail.level)">
|
||||
第{{ categoryDetail.level }}级
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="父级分类">
|
||||
{{ categoryDetail.parent?.name || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="资产数量">
|
||||
<a-badge :count="categoryDetail.asset_count" :max-count="999" />
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="排序">
|
||||
{{ categoryDetail.sort }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="状态">
|
||||
<a-tag :color="categoryDetail.enabled ? 'green' : 'red'">
|
||||
{{ categoryDetail.enabled ? '启用' : '禁用' }}
|
||||
</a-tag>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="颜色">
|
||||
<div v-if="categoryDetail.color" style="display: flex; align-items: center; gap: 8px">
|
||||
<div
|
||||
:style="{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: categoryDetail.color,
|
||||
}"
|
||||
/>
|
||||
<span>{{ categoryDetail.color }}</span>
|
||||
</div>
|
||||
<span v-else>-</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="分类路径">
|
||||
{{ categoryDetail.path || '-' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="描述" :span="2">
|
||||
{{ categoryDetail.description || '-' }}
|
||||
</a-descriptions-item>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Category {
|
||||
id?: number
|
||||
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
|
||||
}
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { fetchCategoryDetail } from '@/api/ops/assetCategory'
|
||||
import type { AssetCategory } from '@/api/ops/assetCategory'
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
category: Category | null
|
||||
categoryId?: number
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'update:visible', visible: boolean): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
// 详情数据
|
||||
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 props = withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
categoryId: undefined,
|
||||
})
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateStr?: string) => {
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
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 '-'
|
||||
const date = new Date(dateStr)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
})
|
||||
try {
|
||||
const date = new Date(dateStr)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
})
|
||||
} catch {
|
||||
return dateStr
|
||||
}
|
||||
}
|
||||
|
||||
// 取消
|
||||
const handleCancel = () => {
|
||||
emit('update:visible', false)
|
||||
// 加载分类详情
|
||||
const loadCategoryDetail = async () => {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 处理对话框可见性变化
|
||||
const handleVisibleChange = (visible: boolean) => {
|
||||
emit('update:visible', visible)
|
||||
}
|
||||
// 监听 visible 变化
|
||||
watch(
|
||||
() => props.visible,
|
||||
(val) => {
|
||||
if (val && props.categoryId) {
|
||||
loadCategoryDetail()
|
||||
} else {
|
||||
categoryDetail.value = null
|
||||
}
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'CategoryDetailDialog',
|
||||
}
|
||||
</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>
|
||||
</script>
|
||||
@@ -1,137 +1,96 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:title="isEdit ? '编辑分类' : '新增分类'"
|
||||
width="600px"
|
||||
@ok="handleOk"
|
||||
v-model:visible="dialogVisible"
|
||||
:title="isEdit ? '编辑分类' : '新建分类'"
|
||||
:width="520"
|
||||
:mask-closable="false"
|
||||
unmount-on-close
|
||||
@cancel="handleCancel"
|
||||
@update:visible="handleVisibleChange"
|
||||
:confirm-loading="submitting"
|
||||
@ok="handleOk"
|
||||
>
|
||||
<a-form :model="form" layout="vertical" ref="formRef">
|
||||
<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"
|
||||
/>
|
||||
</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 ref="formRef" :model="formData" :rules="rules" layout="vertical">
|
||||
<a-form-item field="name" label="分类名称" required>
|
||||
<a-input
|
||||
v-model="formData.name"
|
||||
placeholder="请输入分类名称"
|
||||
:max-length="50"
|
||||
/>
|
||||
</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
|
||||
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="请输入备注"
|
||||
:auto-size="{ minRows: 3, maxRows: 6 }"
|
||||
:max-length="500"
|
||||
:max-length="200"
|
||||
:auto-size="{ minRows: 2, maxRows: 4 }"
|
||||
show-word-limit
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@@ -139,167 +98,176 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, computed } from 'vue'
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { createCategory, updateCategory } from '@/api/ops/assetCategory'
|
||||
|
||||
interface Category {
|
||||
id?: number
|
||||
name?: string
|
||||
code?: string
|
||||
description?: string
|
||||
icon?: string
|
||||
color?: string
|
||||
parent_id?: number
|
||||
sort?: number
|
||||
enabled?: boolean
|
||||
remarks?: string
|
||||
}
|
||||
import type { FormInstance } from '@arco-design/web-vue/es/form'
|
||||
import {
|
||||
createCategory,
|
||||
updateCategory,
|
||||
fetchCategoryTree,
|
||||
fetchCategoryChildren,
|
||||
} from '@/api/ops/assetCategory'
|
||||
import type { AssetCategory, CategoryFormData } from '@/api/ops/assetCategory'
|
||||
|
||||
interface Props {
|
||||
visible: boolean
|
||||
category: Category | null
|
||||
parentCategories: any[]
|
||||
fixedParentId?: number
|
||||
showParentSelector?: boolean
|
||||
category?: AssetCategory | null
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:visible', value: boolean): void
|
||||
(e: 'update:visible', visible: boolean): void
|
||||
(e: 'success'): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
visible: false,
|
||||
category: null,
|
||||
})
|
||||
|
||||
const emit = defineEmits<Emits>()
|
||||
|
||||
const formRef = ref()
|
||||
const submitting = ref(false)
|
||||
const showColorPicker = ref(false)
|
||||
const formRef = ref<FormInstance>()
|
||||
const categoryTreeData = ref<any[]>([])
|
||||
const loading = ref(false)
|
||||
|
||||
// 预设颜色
|
||||
const presetColors = [
|
||||
'#FF0000', '#FF4500', '#FF8C00', '#FFD700', '#FFFF00',
|
||||
'#9ACD32', '#32CD32', '#00FF00', '#00FA9A', '#00CED1',
|
||||
'#1E90FF', '#0000FF', '#8A2BE2', '#9400D3', '#FF00FF',
|
||||
'#FF1493', '#DC143C', '#B22222', '#8B0000', '#800000',
|
||||
]
|
||||
const isEdit = computed(() => !!props.category?.id)
|
||||
|
||||
// 表单数据
|
||||
const form = ref({
|
||||
const dialogVisible = computed({
|
||||
get: () => props.visible,
|
||||
set: (val) => emit('update:visible', val),
|
||||
})
|
||||
|
||||
const defaultFormData = (): CategoryFormData => ({
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
icon: '',
|
||||
color: '',
|
||||
parent_id: undefined as number | undefined,
|
||||
sort: 0,
|
||||
color: '#1890ff',
|
||||
parent_id: null,
|
||||
enabled: true,
|
||||
sort: 0,
|
||||
remarks: '',
|
||||
})
|
||||
|
||||
// 是否为编辑模式
|
||||
const isEdit = computed(() => !!props.category?.id)
|
||||
const formData = ref<CategoryFormData>(defaultFormData())
|
||||
|
||||
// 父级分类下拉搜索过滤
|
||||
const filterParentOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().includes(input.toLowerCase())
|
||||
const rules = {
|
||||
name: [
|
||||
{ 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) => {
|
||||
form.value.color = color
|
||||
showColorPicker.value = false
|
||||
// 加载分类树
|
||||
const loadCategoryTree = async () => {
|
||||
try {
|
||||
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(
|
||||
() => props.visible,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
if (props.category && isEdit.value) {
|
||||
// 编辑模式:填充表单
|
||||
form.value = {
|
||||
name: props.category.name || '',
|
||||
code: props.category.code || '',
|
||||
async (val) => {
|
||||
if (val) {
|
||||
await loadCategoryTree()
|
||||
if (props.category) {
|
||||
// 编辑模式,填充数据
|
||||
formData.value = {
|
||||
id: props.category.id,
|
||||
name: props.category.name,
|
||||
code: props.category.code,
|
||||
description: props.category.description || '',
|
||||
icon: props.category.icon || '',
|
||||
color: props.category.color || '',
|
||||
parent_id: props.category.parent_id,
|
||||
color: props.category.color || '#1890ff',
|
||||
parent_id: props.category.parent_id || null,
|
||||
enabled: props.category.enabled,
|
||||
sort: props.category.sort || 0,
|
||||
enabled: props.category.enabled !== undefined ? props.category.enabled : true,
|
||||
remarks: props.category.remarks || '',
|
||||
}
|
||||
} else {
|
||||
// 新建模式:重置表单
|
||||
form.value = {
|
||||
name: '',
|
||||
code: '',
|
||||
description: '',
|
||||
icon: '',
|
||||
color: '',
|
||||
parent_id: props.fixedParentId,
|
||||
sort: 0,
|
||||
enabled: true,
|
||||
remarks: '',
|
||||
}
|
||||
resetForm()
|
||||
}
|
||||
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 = () => {
|
||||
emit('update:visible', false)
|
||||
resetForm()
|
||||
}
|
||||
|
||||
// 处理对话框可见性变化
|
||||
const handleVisibleChange = (visible: boolean) => {
|
||||
emit('update:visible', visible)
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
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>
|
||||
|
||||
@@ -307,79 +275,4 @@ const handleVisibleChange = (visible: boolean) => {
|
||||
export default {
|
||||
name: 'CategoryFormDialog',
|
||||
}
|
||||
</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>
|
||||
</script>
|
||||
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>
|
||||
<a-button type="primary" @click="handleCreate">
|
||||
新增分类
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
新建分类
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
@@ -27,29 +30,35 @@
|
||||
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
|
||||
</template>
|
||||
|
||||
<!-- 层级 -->
|
||||
<template #level="{ record }">
|
||||
<a-tag :color="getLevelColor(record.level)">
|
||||
第{{ record.level }}级
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 父级分类 -->
|
||||
<template #parent="{ record }">
|
||||
{{ record.parent?.name || '-' }}
|
||||
</template>
|
||||
|
||||
<!-- 颜色标识 -->
|
||||
<template #color="{ record }">
|
||||
<div class="color-cell">
|
||||
<div
|
||||
class="color-block"
|
||||
:style="{ backgroundColor: record.color || '#ccc' }"
|
||||
></div>
|
||||
<span>{{ record.color || '-' }}</span>
|
||||
</div>
|
||||
<!-- 资产数量 -->
|
||||
<template #asset_count="{ record }">
|
||||
<a-badge :count="record.asset_count" :max-count="999" />
|
||||
</template>
|
||||
|
||||
<!-- 是否启用 -->
|
||||
<!-- 状态 -->
|
||||
<template #enabled="{ record }">
|
||||
<a-tag :color="record.enabled ? 'green' : 'red'">
|
||||
{{ record.enabled ? '是' : '否' }}
|
||||
{{ record.enabled ? '启用' : '禁用' }}
|
||||
</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 创建时间 -->
|
||||
<template #created_at="{ record }">
|
||||
{{ formatDateTime(record.created_at) }}
|
||||
</template>
|
||||
|
||||
<!-- 操作 -->
|
||||
<template #actions="{ 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>
|
||||
<a-button type="text" size="small" status="danger" @click="handleDelete(record)">
|
||||
删除
|
||||
<a-button type="text" size="small" @click="handleAddChild(record)">
|
||||
添加子分类
|
||||
</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>
|
||||
</template>
|
||||
</search-table>
|
||||
|
||||
<!-- 分类表单对话框(新增/编辑) -->
|
||||
<!-- 分类表单对话框 -->
|
||||
<category-form-dialog
|
||||
v-model:visible="formVisible"
|
||||
:category="editingCategory"
|
||||
:parent-categories="parentCategories"
|
||||
:fixed-parent-id="fixedParentId"
|
||||
:show-parent-selector="showParentSelector"
|
||||
@success="handleFormSuccess"
|
||||
/>
|
||||
|
||||
<!-- 分类详情对话框 -->
|
||||
<category-detail-dialog
|
||||
v-model:visible="detailVisible"
|
||||
:category="currentCategory"
|
||||
:category-id="currentCategoryId"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -91,21 +102,21 @@ import { Message, Modal } from '@arco-design/web-vue'
|
||||
import { IconPlus } from '@arco-design/web-vue/es/icon'
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
import SearchTable from '@/components/search-table/index.vue'
|
||||
import { searchFormConfig } from './config/search-form'
|
||||
import { columns as columnsConfig } from './config/columns'
|
||||
import {
|
||||
fetchCategoryList,
|
||||
deleteCategory,
|
||||
fetchAllCategories,
|
||||
fetchCategoryDetail,
|
||||
} from '@/api/ops/assetCategory'
|
||||
import type { AssetCategory } from '@/api/ops/assetCategory'
|
||||
import CategoryFormDialog from './components/CategoryFormDialog.vue'
|
||||
import CategoryDetailDialog from './components/CategoryDetailDialog.vue'
|
||||
|
||||
// 状态管理
|
||||
const loading = ref(false)
|
||||
const tableData = ref<any[]>([])
|
||||
const tableData = ref<AssetCategory[]>([])
|
||||
const formModel = ref({
|
||||
keyword: '',
|
||||
parent_id: '',
|
||||
})
|
||||
|
||||
const pagination = reactive({
|
||||
@@ -114,114 +125,46 @@ const pagination = reactive({
|
||||
total: 0,
|
||||
})
|
||||
|
||||
// 父级分类列表(用于下拉选择)
|
||||
const parentCategories = ref<any[]>([])
|
||||
|
||||
// 表单项配置
|
||||
const formItems = computed<FormItem[]>(() => [
|
||||
{
|
||||
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 formItems = computed<FormItem[]>(() => searchFormConfig)
|
||||
|
||||
// 表格列配置
|
||||
const columns = computed(() => [
|
||||
{
|
||||
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 columns = computed(() => columnsConfig)
|
||||
|
||||
// 当前选中的分类
|
||||
const currentCategory = ref<any>(null)
|
||||
const editingCategory = ref<any>(null)
|
||||
const currentCategoryId = ref<number | undefined>(undefined)
|
||||
const editingCategory = ref<AssetCategory | null>(null)
|
||||
|
||||
// 对话框可见性
|
||||
const formVisible = 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 fetchParentCategories = async () => {
|
||||
// 格式化日期时间
|
||||
const formatDateTime = (dateStr?: string) => {
|
||||
if (!dateStr) return '-'
|
||||
try {
|
||||
const res = await fetchAllCategories()
|
||||
parentCategories.value = res.details || []
|
||||
} catch (error) {
|
||||
console.error('获取父级分类列表失败:', error)
|
||||
const date = new Date(dateStr)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
year: 'numeric',
|
||||
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
|
||||
|
||||
try {
|
||||
const params: any = {
|
||||
const params = {
|
||||
page: pagination.current,
|
||||
page_size: pagination.pageSize,
|
||||
keyword: formModel.value.keyword || undefined,
|
||||
parent_id: formModel.value.parent_id || undefined,
|
||||
}
|
||||
|
||||
const res = await fetchCategoryList(params)
|
||||
@@ -266,7 +208,6 @@ const handleFormModelUpdate = (value: any) => {
|
||||
const handleReset = () => {
|
||||
formModel.value = {
|
||||
keyword: '',
|
||||
parent_id: '',
|
||||
}
|
||||
pagination.current = 1
|
||||
fetchCategories()
|
||||
@@ -284,53 +225,38 @@ const handleRefresh = () => {
|
||||
Message.success('数据已刷新')
|
||||
}
|
||||
|
||||
// 新增分类
|
||||
// 新建分类
|
||||
const handleCreate = () => {
|
||||
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
|
||||
}
|
||||
|
||||
// 编辑分类
|
||||
const handleEdit = (record: any) => {
|
||||
console.log('编辑分类:', record)
|
||||
const handleEdit = (record: AssetCategory) => {
|
||||
editingCategory.value = record
|
||||
formVisible.value = true
|
||||
}
|
||||
|
||||
// 添加子分类
|
||||
const handleAddChild = (record: AssetCategory) => {
|
||||
editingCategory.value = {
|
||||
parent_id: record.id,
|
||||
} as AssetCategory
|
||||
formVisible.value = true
|
||||
}
|
||||
|
||||
// 详情
|
||||
const handleDetail = async (record: any) => {
|
||||
console.log('查看详情:', record)
|
||||
try {
|
||||
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 handleDetail = (record: AssetCategory) => {
|
||||
currentCategoryId.value = record.id
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
// 删除分类
|
||||
const handleDelete = async (record: any) => {
|
||||
console.log('删除分类:', record)
|
||||
const handleDelete = async (record: AssetCategory) => {
|
||||
try {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: `确认删除分类 ${record.name} 吗?`,
|
||||
content: `确认删除分类「${record.name}」吗?如果该分类下存在子分类或资产,将无法删除。`,
|
||||
onOk: async () => {
|
||||
const res = await deleteCategory(record.id)
|
||||
if (res.code === 0) {
|
||||
@@ -348,21 +274,18 @@ const handleDelete = async (record: any) => {
|
||||
|
||||
// 表单成功回调
|
||||
const handleFormSuccess = () => {
|
||||
formVisible.value = false
|
||||
fetchCategories()
|
||||
fetchParentCategories()
|
||||
}
|
||||
|
||||
// 初始化加载数据
|
||||
onMounted(() => {
|
||||
fetchParentCategories()
|
||||
fetchCategories()
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'AssetClassify',
|
||||
name: 'AssetCategory',
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -370,18 +293,4 @@ export default {
|
||||
.container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.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>
|
||||
</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>
|
||||
<a-button @click="handleExport">
|
||||
导出
|
||||
</a-button>
|
||||
</template>
|
||||
|
||||
<!-- 序号 -->
|
||||
@@ -66,13 +63,6 @@
|
||||
</template>
|
||||
</search-table>
|
||||
|
||||
<!-- 设备表单对话框(新增/编辑) -->
|
||||
<device-form-dialog
|
||||
v-model:visible="formVisible"
|
||||
:device="editingDevice"
|
||||
@success="handleFormSuccess"
|
||||
/>
|
||||
|
||||
<!-- 设备详情对话框 -->
|
||||
<device-detail-dialog
|
||||
v-model:visible="detailVisible"
|
||||
@@ -83,6 +73,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
import SearchTable from '@/components/search-table/index.vue'
|
||||
@@ -94,8 +85,9 @@ import {
|
||||
getAssetStatusColor,
|
||||
assetStatusOptions,
|
||||
} 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)
|
||||
@@ -204,17 +196,15 @@ const columns = computed(() => [
|
||||
{
|
||||
title: '操作',
|
||||
slotName: 'actions',
|
||||
width: 280,
|
||||
width: 220,
|
||||
fixed: 'right' as const,
|
||||
},
|
||||
])
|
||||
|
||||
// 当前选中的设备
|
||||
const currentDevice = ref<any>(null)
|
||||
const editingDevice = ref<any>(null)
|
||||
|
||||
// 对话框可见性
|
||||
const formVisible = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
|
||||
// 获取设备列表
|
||||
@@ -276,16 +266,17 @@ const handleRefresh = () => {
|
||||
Message.success('数据已刷新')
|
||||
}
|
||||
|
||||
// 新增设备
|
||||
// 新增设备 - 跳转到新增页面
|
||||
const handleCreate = () => {
|
||||
editingDevice.value = null
|
||||
formVisible.value = true
|
||||
router.push('/assets/device/form')
|
||||
}
|
||||
|
||||
// 编辑设备
|
||||
// 编辑设备 - 跳转到编辑页面
|
||||
const handleEdit = (record: any) => {
|
||||
editingDevice.value = record
|
||||
formVisible.value = true
|
||||
router.push({
|
||||
path: '/assets/device/form',
|
||||
query: { id: record.id },
|
||||
})
|
||||
}
|
||||
|
||||
// 详情
|
||||
@@ -335,7 +326,7 @@ const handleExport = async () => {
|
||||
keyword: formModel.value.keyword || undefined,
|
||||
status: formModel.value.status || undefined,
|
||||
})
|
||||
|
||||
|
||||
if (res.code === 0 && res.details?.data) {
|
||||
const data = res.details.data
|
||||
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) => {
|
||||
if (value === null || value === undefined || value === '') return '-'
|
||||
@@ -389,7 +374,7 @@ onMounted(() => {
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DeviceManage',
|
||||
name: 'DeviceList',
|
||||
}
|
||||
</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>
|
||||
</search-table>
|
||||
|
||||
<!-- 供应商表单对话框(新增/编辑) -->
|
||||
<supplier-form-dialog
|
||||
v-model:visible="formVisible"
|
||||
:supplier="editingSupplier"
|
||||
@success="handleFormSuccess"
|
||||
/>
|
||||
|
||||
<!-- 供应商详情对话框 -->
|
||||
<supplier-detail-dialog
|
||||
v-model:visible="detailVisible"
|
||||
@@ -75,6 +68,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Message, Modal } from '@arco-design/web-vue'
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
import SearchTable from '@/components/search-table/index.vue'
|
||||
@@ -83,8 +77,9 @@ import {
|
||||
deleteSupplier,
|
||||
fetchSupplierDetail,
|
||||
} 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 = [
|
||||
@@ -200,10 +195,8 @@ const columns = computed(() => [
|
||||
|
||||
// 当前选中的供应商
|
||||
const currentSupplier = ref<any>(null)
|
||||
const editingSupplier = ref<any>(null)
|
||||
|
||||
// 对话框可见性
|
||||
const formVisible = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
|
||||
// 获取供应商列表
|
||||
@@ -265,16 +258,17 @@ const handleRefresh = () => {
|
||||
Message.success('数据已刷新')
|
||||
}
|
||||
|
||||
// 新增供应商
|
||||
// 新增供应商 - 跳转到新增页面
|
||||
const handleCreate = () => {
|
||||
editingSupplier.value = null
|
||||
formVisible.value = true
|
||||
router.push('/assets/supplier/form')
|
||||
}
|
||||
|
||||
// 编辑供应商
|
||||
// 编辑供应商 - 跳转到编辑页面(通过 query 参数传递 id)
|
||||
const handleEdit = (record: any) => {
|
||||
editingSupplier.value = record
|
||||
formVisible.value = true
|
||||
router.push({
|
||||
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 colorMap: Record<string, string> = {
|
||||
@@ -373,7 +361,7 @@ onMounted(() => {
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'SupplierManage',
|
||||
name: 'SupplierList',
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
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 SearchTable from '@/components/search-table/index.vue'
|
||||
import type { FormItem } from '@/components/search-form/types'
|
||||
@@ -378,25 +378,36 @@ const handleView = (record: ReviewListItem) => {
|
||||
}
|
||||
|
||||
// 审核通过
|
||||
const handleApprove = async (record: ReviewListItem) => {
|
||||
try {
|
||||
loading.value = true
|
||||
const res: any = await approveReview({
|
||||
resource_type: record.type,
|
||||
id: record.resource.id,
|
||||
})
|
||||
if (res?.code === 0) {
|
||||
Message.success('审核通过')
|
||||
await fetchData()
|
||||
} else {
|
||||
Message.error(res?.message || '审核失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('审核失败:', error)
|
||||
Message.error('审核失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
const handleApprove = (record: ReviewListItem) => {
|
||||
const resourceTitle = getResourceTitle(record)
|
||||
const resourceTypeText = getResourceTypeText(record.type)
|
||||
|
||||
Modal.confirm({
|
||||
title: '确认审核通过',
|
||||
content: `确定要通过该${resourceTypeText}的审核吗?\n标题:${resourceTitle}`,
|
||||
okText: '确认通过',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const res: any = await approveReview({
|
||||
resource_type: record.type,
|
||||
id: record.resource.id,
|
||||
})
|
||||
if (res?.code === 0) {
|
||||
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)
|
||||
}
|
||||
|
||||
if (res.code === 200) {
|
||||
if (res.code === 0) {
|
||||
Message.success(isEdit.value ? '编辑成功' : '创建成功')
|
||||
emit('success')
|
||||
emit('update:visible', false)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:pagination="pagination"
|
||||
title="分类管理"
|
||||
search-button-text="查询"
|
||||
reset-button-text="重置"
|
||||
@@ -13,6 +14,8 @@
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
@refresh="handleRefresh"
|
||||
@page-change="handlePageChange"
|
||||
@page-size-change="handlePageSizeChange"
|
||||
>
|
||||
<template #toolbar-left>
|
||||
<a-button type="primary" @click="handleCreate">
|
||||
@@ -66,12 +69,20 @@ import CategoryFormDialog from './components/CategoryFormDialog.vue'
|
||||
|
||||
// 状态管理
|
||||
const loading = ref(false)
|
||||
const tableData = ref<Category[]>([])
|
||||
const allData = ref<Category[]>([]) // 存储全量数据
|
||||
const tableData = ref<Category[]>([]) // 当前页数据
|
||||
const formModel = ref({
|
||||
keyword: '',
|
||||
type: '',
|
||||
})
|
||||
|
||||
// 分页状态
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
// 表单项配置
|
||||
const formItems = computed<FormItem[]>(() => [
|
||||
{
|
||||
@@ -122,6 +133,12 @@ const columns = computed(() => [
|
||||
width: 120,
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
dataIndex: 'sort_order',
|
||||
width: 80,
|
||||
align: 'center' as const,
|
||||
},
|
||||
{
|
||||
title: '备注信息',
|
||||
dataIndex: 'remarks',
|
||||
@@ -162,6 +179,14 @@ const getTypeColor = (type: string) => {
|
||||
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 () => {
|
||||
loading.value = true
|
||||
@@ -180,20 +205,29 @@ const fetchCategories = async () => {
|
||||
|
||||
// 如果有关键词搜索,进行过滤
|
||||
if (formModel.value.keyword) {
|
||||
data = data.filter((item: Category) =>
|
||||
data = data.filter((item: Category) =>
|
||||
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 {
|
||||
Message.error(res.message || '获取分类列表失败')
|
||||
allData.value = []
|
||||
tableData.value = []
|
||||
pagination.value.total = 0
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取分类列表失败:', error)
|
||||
Message.error('获取分类列表失败')
|
||||
allData.value = []
|
||||
tableData.value = []
|
||||
pagination.value.total = 0
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -224,6 +258,19 @@ const handleRefresh = () => {
|
||||
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 = () => {
|
||||
editingCategory.value = null
|
||||
@@ -244,7 +291,7 @@ const handleDelete = async (record: Category) => {
|
||||
onOk: async () => {
|
||||
try {
|
||||
const res: any = await deleteCategory(record.id)
|
||||
if (res.code === 200) {
|
||||
if (res.code === 0) {
|
||||
Message.success('删除成功')
|
||||
fetchCategories()
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user