Browse Source

工作台接口对接

hurx 4 months ago
parent
commit
fa529afc98

+ 5 - 3
src/api/pc/bank/bank.ts

@@ -3,9 +3,11 @@ import request from '@/utils/request';
 /**
  * 查询银行信息列表
  */
-export function getBankList() {
+
+export function getBankList(params?: any) {
   return request({
-    url: `/system/bank/list/`,
-    method: 'get'
+    url: '/system/pcBank/list',
+    method: 'get',
+    params: params
   });
 }

+ 1 - 1
src/api/pc/bank/types.ts

@@ -1,7 +1,7 @@
 /**
  * 银行信息
  */
-export interface EnterpriseInfo {
+export interface BankInfo {
   id?: number;
   bnId?: string;
   bnName?: string;

+ 5 - 5
src/api/pc/enterprise/index.ts

@@ -97,7 +97,7 @@ export function setDefaultAddress(data: { id: number }) {
  */
 export function getInvoiceList(params?: any) {
   return request({
-    url: '/customer/enterprise/invoice/list',
+    url: '/customer/pcInvoice/list',
     method: 'get',
     params: params
   });
@@ -108,7 +108,7 @@ export function getInvoiceList(params?: any) {
  */
 export function getInvoiceInfo(id: number) {
   return request({
-    url: `/customer/enterprise/invoice/${id}`,
+    url: `/customer/pcInvoice/invoice/${id}`,
     method: 'get'
   });
 }
@@ -118,7 +118,7 @@ export function getInvoiceInfo(id: number) {
  */
 export function addInvoice(data: InvoiceInfo) {
   return request({
-    url: '/customer/enterprise/invoice',
+    url: '/customer/pcInvoice',
     method: 'post',
     data: data
   });
@@ -129,7 +129,7 @@ export function addInvoice(data: InvoiceInfo) {
  */
 export function updateInvoice(data: InvoiceInfo) {
   return request({
-    url: '/customer/enterprise/invoice',
+    url: '/customer/pcInvoice',
     method: 'put',
     data: data
   });
@@ -140,7 +140,7 @@ export function updateInvoice(data: InvoiceInfo) {
  */
 export function deleteInvoice(ids: number[]) {
   return request({
-    url: `/customer/enterprise/invoice/${ids.join(',')}`,
+    url: `/customer/pcInvoice/${ids.join(',')}`,
     method: 'delete'
   });
 }

+ 1 - 1
src/api/pc/enterprise/invoice.ts

@@ -5,7 +5,7 @@ import request from '@/utils/request';
  */
 export function getInvoiceList(params?: any) {
   return request({
-    url: '/pc/enterprise/invoice/list',
+    url: '/customer/pcInvoice/list',
     method: 'get',
     params: params
   });

+ 17 - 6
src/api/pc/enterprise/order.ts

@@ -8,7 +8,7 @@ import { OrderMain, OrderProduct, OrderStatusStats } from './orderTypes';
  */
 export function getOrderList(params?: any) {
   return request({
-    url: '/pcOrder/list',
+    url: '/order/pcOrder/list',
     method: 'get',
     params: params
   });
@@ -29,7 +29,7 @@ export function getOrderStatusStats() {
  */
 export function getOrderInfo(id: number) {
   return request({
-    url: `/pc/enterprise/order/${id}`,
+    url: `/order/pcOrder/${id}`,
     method: 'get'
   });
 }
@@ -39,7 +39,7 @@ export function getOrderInfo(id: number) {
  */
 export function getOrderProducts(orderIds: number[]) {
   return request({
-    url: '/pc/enterprise/order/products',
+    url: '/order/pcOrder/products',
     method: 'get',
     params: { orderIds: orderIds.join(',') }
   });
@@ -50,7 +50,7 @@ export function getOrderProducts(orderIds: number[]) {
  */
 export function cancelOrder(data: { id: number; orderStatus?: string }) {
   return request({
-    url: '/pc/enterprise/order/cancel',
+    url: '/order/pcOrder/cancel',
     method: 'put',
     data: data
   });
@@ -62,7 +62,7 @@ export function cancelOrder(data: { id: number; orderStatus?: string }) {
  */
 export function checkOrderStatus(data: { id: number; checkStatus: string }) {
   return request({
-    url: '/pc/enterprise/order/checkStatus',
+    url: '/order/pcOrder/checkStatus',
     method: 'put',
     data: data
   });
@@ -73,7 +73,18 @@ export function checkOrderStatus(data: { id: number; checkStatus: string }) {
  */
 export function deleteOrder(ids: number[]) {
   return request({
-    url: `/pc/enterprise/order/${ids.join(',')}`,
+    url: `/order/pcOrder/${ids.join(',')}`,
     method: 'delete'
   });
 }
+
+/**
+ * 新增订单评价
+ */
+export function addOrderEvaluation(data: any) {
+  return request({
+    url: '/order/pcOrderEvaluation',
+    method: 'post',
+    data: data
+  });
+}

+ 5 - 5
src/api/pc/enterprise/orderReturn.ts

@@ -8,7 +8,7 @@ import { OrderReturn } from './orderReturnTypes';
  */
 export function getOrderReturnList(params?: any) {
   return request({
-    url: '/pc/enterprise/orderReturn/list',
+    url: '/pcOrder/orderReturn/list',
     method: 'get',
     params: params
   });
@@ -19,7 +19,7 @@ export function getOrderReturnList(params?: any) {
  */
 export function getOrderReturnInfo(id: number) {
   return request({
-    url: `/pc/enterprise/orderReturn/${id}`,
+    url: `/pcOrder/orderReturn/${id}`,
     method: 'get'
   });
 }
@@ -30,7 +30,7 @@ export function getOrderReturnInfo(id: number) {
  */
 export function addOrderReturn(data: OrderReturn) {
   return request({
-    url: '/pc/enterprise/orderReturn',
+    url: '/pcOrder/orderReturn',
     method: 'post',
     data: data
   });
@@ -42,7 +42,7 @@ export function addOrderReturn(data: OrderReturn) {
  */
 export function updateOrderReturn(data: OrderReturn) {
   return request({
-    url: '/pc/enterprise/orderReturn',
+    url: '/pcOrder/orderReturn',
     method: 'put',
     data: data
   });
@@ -54,7 +54,7 @@ export function updateOrderReturn(data: OrderReturn) {
  */
 export function deleteOrderReturn(ids: number[]) {
   return request({
-    url: `/pc/enterprise/orderReturn/${ids.join(',')}`,
+    url: `/pcOrder/orderReturn/${ids.join(',')}`,
     method: 'delete'
   });
 }

+ 72 - 17
src/api/pc/organization/index.ts

@@ -7,7 +7,7 @@ import request from '@/utils/request';
  */
 export function getDeptTree() {
   return request({
-    url: '/pc/organization/dept/tree',
+    url: '/customer/organization/dept/tree',
     method: 'get'
   });
 }
@@ -17,7 +17,7 @@ export function getDeptTree() {
  */
 export function getDeptList(params?: any) {
   return request({
-    url: '/pc/organization/dept/list',
+    url: '/customer/organization/dept/list',
     method: 'get',
     params: params
   });
@@ -28,7 +28,7 @@ export function getDeptList(params?: any) {
  */
 export function getDeptInfo(id: number) {
   return request({
-    url: `/pc/organization/dept/${id}`,
+    url: `/customer/organization/dept/${id}`,
     method: 'get'
   });
 }
@@ -38,7 +38,7 @@ export function getDeptInfo(id: number) {
  */
 export function addDept(data: any) {
   return request({
-    url: '/pc/organization/dept',
+    url: '/customer/organization/dept',
     method: 'post',
     data: data
   });
@@ -49,7 +49,7 @@ export function addDept(data: any) {
  */
 export function updateDept(data: any) {
   return request({
-    url: '/pc/organization/dept',
+    url: '/customer/organization/dept',
     method: 'put',
     data: data
   });
@@ -60,7 +60,7 @@ export function updateDept(data: any) {
  */
 export function deleteDept(ids: number[]) {
   return request({
-    url: `/pc/organization/dept/${ids.join(',')}`,
+    url: `/customer/organization/dept/${ids.join(',')}`,
     method: 'delete'
   });
 }
@@ -72,7 +72,7 @@ export function deleteDept(ids: number[]) {
  */
 export function getCurrentUserInfo() {
   return request({
-    url: '/pc/organization/contact/current',
+    url: '/customer/organization/contact/current',
     method: 'get'
   });
 }
@@ -82,7 +82,7 @@ export function getCurrentUserInfo() {
  */
 export function getContactList(params?: any) {
   return request({
-    url: '/pc/organization/contact/list',
+    url: '/customer/organization/contact/list',
     method: 'get',
     params: params
   });
@@ -93,7 +93,7 @@ export function getContactList(params?: any) {
  */
 export function getContactInfo(id: number) {
   return request({
-    url: `/pc/organization/contact/${id}`,
+    url: `/customer/organization/contact/${id}`,
     method: 'get'
   });
 }
@@ -103,7 +103,7 @@ export function getContactInfo(id: number) {
  */
 export function addContact(data: any) {
   return request({
-    url: '/pc/organization/contact',
+    url: '/customer/organization/contact',
     method: 'post',
     data: data
   });
@@ -114,7 +114,7 @@ export function addContact(data: any) {
  */
 export function updateContact(data: any) {
   return request({
-    url: '/pc/organization/contact',
+    url: '/customer/organization/contact',
     method: 'put',
     data: data
   });
@@ -125,7 +125,7 @@ export function updateContact(data: any) {
  */
 export function deleteContact(ids: number[]) {
   return request({
-    url: `/pc/organization/contact/${ids.join(',')}`,
+    url: `/customer/organization/contact/${ids.join(',')}`,
     method: 'delete'
   });
 }
@@ -137,7 +137,7 @@ export function deleteContact(ids: number[]) {
  */
 export function getRoleList(params?: any) {
   return request({
-    url: '/pc/organization/role/list',
+    url: '/system/organization/role/list',
     method: 'get',
     params: params
   });
@@ -148,7 +148,7 @@ export function getRoleList(params?: any) {
  */
 export function getRoleInfo(id: number) {
   return request({
-    url: `/pc/organization/role/${id}`,
+    url: `/system/organization/role/${id}`,
     method: 'get'
   });
 }
@@ -158,7 +158,7 @@ export function getRoleInfo(id: number) {
  */
 export function addRole(data: any) {
   return request({
-    url: '/pc/organization/role',
+    url: '/system/organization/role',
     method: 'post',
     data: data
   });
@@ -169,7 +169,7 @@ export function addRole(data: any) {
  */
 export function updateRole(data: any) {
   return request({
-    url: '/pc/organization/role',
+    url: '/system/organization/role',
     method: 'put',
     data: data
   });
@@ -180,7 +180,62 @@ export function updateRole(data: any) {
  */
 export function deleteRole(ids: number[]) {
   return request({
-    url: `/pc/organization/role/${ids.join(',')}`,
+    url: `/system/organization/role/${ids.join(',')}`,
+    method: 'delete'
+  });
+}
+
+// ==================== 人员管理 ====================
+
+/**
+ * 查询人员列表
+ */
+export function getComStaffList(params?: any) {
+  return request({
+    url: '/system/organization/comStaff/list',
+    method: 'get',
+    params: params
+  });
+}
+
+/**
+ * 查询人员详情
+ */
+export function getComStaffInfo(id: number) {
+  return request({
+    url: `/system/organization/comStaff/${id}`,
+    method: 'get'
+  });
+}
+
+/**
+ * 新增人员
+ */
+export function addComStaff(data: any) {
+  return request({
+    url: '/system/organization/comStaff',
+    method: 'post',
+    data: data
+  });
+}
+
+/**
+ * 修改人员
+ */
+export function updateComStaff(data: any) {
+  return request({
+    url: '/system/organization/comStaff',
+    method: 'put',
+    data: data
+  });
+}
+
+/**
+ * 删除人员
+ */
+export function deleteComStaff(ids: number[]) {
+  return request({
+    url: `/system/organization/comStaff/${ids.join(',')}`,
     method: 'delete'
   });
 }

+ 43 - 43
src/api/pc/organization/types.ts

@@ -1,26 +1,26 @@
 // 部门信息
 export interface DeptInfo {
-  deptId: number;           // 部门ID
-  deptNo?: string;          // 部门编号
-  deptName: string;         // 部门名称
-  parentId?: number;        // 父部门ID
-  ancestors?: string;       // 祖级列表
-  customerId: number;       // 客户ID
-  yearlyBudget?: number;    // 年度预算
-  usedBudget?: number;      // 已使用预算
-  monthLimit?: number;      // 月度限额
+  deptId: number; // 部门ID
+  deptNo?: string; // 部门编号
+  deptName: string; // 部门名称
+  parentId?: number; // 父部门ID
+  ancestors?: string; // 祖级列表
+  customerId: number; // 客户ID
+  yearlyBudget?: number; // 年度预算
+  usedBudget?: number; // 已使用预算
+  monthLimit?: number; // 月度限额
   monthUsedBudget?: number; // 月度已用预算
-  bindStatus?: string;      // 绑定状态
-  bindAddress?: string;     // 绑定地址
-  deptManage?: string;      // 部门负责人
-  isLimit?: string;         // 是否限制预算
-  selectYear?: string;      // 所选年份
-  expenseType?: string;     // 费用类型
+  bindStatus?: string; // 绑定状态
+  bindAddress?: string; // 绑定地址
+  deptManage?: string; // 部门负责人
+  isLimit?: string; // 是否限制预算
+  selectYear?: string; // 所选年份
+  expenseType?: string; // 费用类型
   residueYearlyBudget?: number; // 年度剩余预算
-  recharge?: number;        // 充值金额
-  status?: string;          // 状态
-  remark?: string;          // 备注
-  createTime?: string;      // 创建时间
+  recharge?: number; // 充值金额
+  status?: string; // 状态
+  remark?: string; // 备注
+  createTime?: string; // 创建时间
 }
 
 // 部门树节点
@@ -33,35 +33,35 @@ export interface DeptTreeNode {
 
 // 联系人信息
 export interface ContactInfo {
-  id: number;               // 联系人ID
-  customerId: number;       // 客户ID
-  contactName: string;      // 联系人姓名
+  id: number; // 联系人ID
+  customerId: number; // 客户ID
+  contactName: string; // 联系人姓名
   customLoginName?: string; // 自定义登录名
-  phone: string;            // 手机号码
-  officePhone?: string;     // 办公电话
-  officePhoneTwo?: string;  // 办公电话2
-  gender?: string;          // 性别
-  roleId?: number;          // 角色ID
-  deptId?: number;          // 部门ID
-  email?: string;           // 邮箱
-  birthday?: string;        // 生日
-  isPrimary?: string;       // 是否主联系人
-  addressDetail?: string;   // 详细地址
+  phone: string; // 手机号码
+  officePhone?: string; // 办公电话
+  officePhoneTwo?: string; // 办公电话2
+  gender?: string; // 性别
+  roleId?: number; // 角色ID
+  deptId?: number; // 部门ID
+  email?: string; // 邮箱
+  birthday?: string; // 生日
+  isPrimary?: string; // 是否主联系人
+  addressDetail?: string; // 详细地址
   addressProvince?: string; // 所在省编码
-  addressCity?: string;     // 所在市编码
-  addressCounty?: string;   // 所在区县编码
+  addressCity?: string; // 所在市编码
+  addressCounty?: string; // 所在区县编码
   provincialCityCounty?: string; // 省市区
-  status?: string;          // 状态
-  remark?: string;          // 备注
+  status?: string; // 状态
+  remark?: string; // 备注
 }
 
 // 角色信息
 export interface RoleInfo {
-  roleId: number;           // 角色ID
-  roleName: string;         // 角色名称
-  roleKey?: string;         // 角色权限字符
-  roleSort?: number;        // 排序
-  dataScope?: string;       // 数据范围
-  status?: string;          // 状态
-  remark?: string;          // 备注
+  roleId: number; // 角色ID
+  roleName: string; // 角色名称
+  roleKey?: string; // 角色权限字符
+  roleSort?: number; // 排序
+  dataScope?: string; // 数据范围
+  status?: string; // 状态
+  remark?: string; // 备注
 }

+ 44 - 0
src/api/pc/valueAdded/index.ts

@@ -0,0 +1,44 @@
+import request from '@/utils/request';
+
+/**
+ * 查询投诉建议suggestions列表
+ */
+export function getComplaintsSuggestionsList(params?: any) {
+  return request({
+    url: '/customer/pcComplaintsSuggestions/list',
+    method: 'get',
+    params: params
+  });
+}
+
+/**
+ * 新增投诉建议
+ */
+export function addComplaintsSuggestions(data: any) {
+  return request({
+    url: '/customer/pcComplaintsSuggestions',
+    method: 'post',
+    data: data
+  });
+}
+
+/**
+ * 修改投诉建议
+ */
+export function updateComplaintsSuggestions(data: any) {
+  return request({
+    url: '/customer/pcComplaintsSuggestions',
+    method: 'put',
+    data: data
+  });
+}
+
+/**
+ * 删除投诉建议
+ */
+export function deleteComplaintsSuggestions(ids: number[]) {
+  return request({
+    url: `/customer/pcComplaintsSuggestions/${ids.join(',')}`,
+    method: 'delete'
+  });
+}

+ 19 - 0
src/api/pc/valueAdded/types.ts

@@ -0,0 +1,19 @@
+// 角色信息
+export interface ComplaintsSuggestions {
+  id: number; // 建议ID
+  complainNo: string; // 编号
+  customerId?: number; // 企业id
+  userId?: number; // 用户id
+  userNo?: string; // 用户编号
+  userName?: string; // 用户称号
+  phone?: string; // 电话
+  feedbackType?: string; // 反馈类型
+  relatedPictures?: string; // 相关图片
+  processTime?: string; // 处理时间
+  processStaff?: string; // 处理人
+  processResult?: string; // 处理结果
+  processFeedback?: string; // 处理反馈
+  processEvaluation?: number; // 处理评价
+  resultFeedback?: string;
+  remark?: string; // 备注
+}

+ 5 - 1
src/layout/components/header.vue

@@ -9,7 +9,7 @@
       <div class="header-box flex-row-start">
         <div v-if="!isLoggedIn" class="header-text" @click="goToLogin" style="cursor: pointer">请登录</div>
         <div v-if="!isLoggedIn" class="header-text hig">免费注册</div>
-        <div class="header-text">我的订单</div>
+        <div class="header-text" @click="goToMyOrder">我的订单</div>
         <div class="header-text">会员中心</div>
         <div class="header-text">人才招聘</div>
         <div class="header-text">帮助中心</div>
@@ -33,6 +33,10 @@ const isLoggedIn = computed(() => !!userStore.token);
 const goToLogin = () => {
   router.push('/login');
 };
+
+const goToMyOrder = () => {
+  router.push('/enterprise/companyInfo');
+};
 </script>
 
 <style lang="scss" scoped>

+ 3 - 0
src/types/components.d.ts

@@ -53,12 +53,15 @@ declare module 'vue' {
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
+    ElSelectTree: typeof import('element-plus/es')['ElSelectTree']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
+    ElTree: typeof import('element-plus/es')['ElTree']
+    ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
     ElUpload: typeof import('element-plus/es')['ElUpload']
     EmptyState: typeof import('./../components/EmptyState/index.vue')['default']
     FileUpload: typeof import('./../components/FileUpload/index.vue')['default']

+ 45 - 2
src/views/enterprise/companyInfo/edit.vue

@@ -59,8 +59,14 @@
           <div class="section-title"><i class="title-bar"></i>办公信息</div>
           <el-row :gutter="20">
             <el-col :span="12">
-              <el-form-item label="主要办公地址">
-                <el-select v-model="form.officeProvince" placeholder="请选择" style="width: 100%"> </el-select>
+              <el-form-item label="主要办公地址" prop="provincialCityCounty">
+                <el-cascader
+                  v-model="form.officeProvince"
+                  :options="regionData as any"
+                  placeholder="请选择省/市/区"
+                  style="width: 100%"
+                  @change="handleRegionChange"
+                />
               </el-form-item>
             </el-col>
             <el-col :span="12">
@@ -172,6 +178,8 @@ import { ArrowLeft, Plus } from '@element-plus/icons-vue';
 import { ElMessage } from 'element-plus';
 import { getEnterpriseInfo, updateEnterpriseInfo } from '@/api/pc/enterprise';
 
+import { regionData } from 'element-china-area-data';
+
 const router = useRouter();
 const formRef = ref();
 
@@ -183,6 +191,7 @@ const form = reactive({
   detailAddress: '',
   businessScope: '',
   officeProvince: '',
+  regionCodes: [],
   officeAddress: '',
   industry: '',
   scale: '',
@@ -213,6 +222,7 @@ const loadEnterpriseInfo = async () => {
       form.detailAddress = businessInfo.businessAddress || '';
       form.businessScope = businessInfo.bussinessRange || '';
       form.officeAddress = data.address || '';
+      form.officeProvince = data.provincialCityCounty || '';
       form.industry = data.industryCategory || '';
       form.scale = data.enterpriseScale || '';
       form.phone = data.landline || '';
@@ -227,6 +237,39 @@ const loadEnterpriseInfo = async () => {
   }
 };
 
+// 处理地区选择变化
+const handleRegionChange = (value: string[]) => {
+  if (value && value.length === 3) {
+    // 根据选中的代码查找对应的名称
+
+    // 根据编码获取名称
+    const names: string[] = [];
+    if (value[0]) {
+      const province = regionData.find((item: any) => item.value === value[0]);
+      if (province) {
+        names.push(province.label);
+
+        if (value[1] && province.children) {
+          const city = province.children.find((item: any) => item.value === value[1]);
+          if (city) {
+            names.push(city.label);
+
+            if (value[2] && city.children) {
+              const county = city.children.find((item: any) => item.value === value[2]);
+              if (county) {
+                names.push(county.label);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // 将省市区名称用斜杠连接
+    form.officeProvince = names.join('/');
+  }
+};
+
 onMounted(() => {
   loadEnterpriseInfo();
 });

+ 156 - 58
src/views/enterprise/invoiceManage/index.vue

@@ -4,7 +4,9 @@
     <!-- 搜索栏 -->
     <div class="search-bar">
       <el-input v-model="queryParams.keyword" placeholder="搜索" style="width: 200px" clearable>
-        <template #prefix><el-icon><Search /></el-icon></template>
+        <template #prefix
+          ><el-icon><Search /></el-icon
+        ></template>
       </el-input>
       <el-select v-model="queryParams.searchType" placeholder="账户名称" style="width: 120px">
         <el-option label="账户名称" value="accountName" />
@@ -22,7 +24,7 @@
       <el-table-column prop="phone" label="电话" width="120" :resizable="false" />
       <el-table-column label="操作" width="120" fixed="right" :resizable="false">
         <template #default="{ row }">
-          <div style="display: flex; gap: 8px; justify-content: center;">
+          <div style="display: flex; gap: 8px; justify-content: center">
             <el-button type="primary" link @click="handleEdit(row)">修改</el-button>
             <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
           </div>
@@ -32,16 +34,28 @@
     <!-- 分页 -->
     <div class="pagination-wrap">
       <span class="total-text">共计 {{ total }} 条</span>
-      <el-pagination v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize"
-        :page-sizes="[10, 20, 50]" :total="total" layout="prev, pager, next, sizes, jumper"
-        @size-change="handleQuery" @current-change="handleQuery" />
+      <el-pagination
+        v-model:current-page="queryParams.pageNum"
+        v-model:page-size="queryParams.pageSize"
+        :page-sizes="[10, 20, 50]"
+        :total="total"
+        layout="prev, pager, next, sizes, jumper"
+        @size-change="handleQuery"
+        @current-change="handleQuery"
+      />
     </div>
     <!-- 新增/编辑弹窗 -->
     <el-dialog v-model="dialogVisible" :title="dialogTitle" width="550px">
       <el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
-        <el-form-item label="发票抬头" prop="invoiceTitle"><el-input v-model="form.invoiceTitle" placeholder="请输入发票抬头" /></el-form-item>
-        <el-form-item label="纳税人识别号" prop="taxNo"><el-input v-model="form.taxNo" placeholder="请输入纳税人识别号" /></el-form-item>
-        <el-form-item label="开户银行" prop="bankName"><el-input v-model="form.bankName" placeholder="请输入开户银行" /></el-form-item>
+        <el-form-item label="发票抬头" prop="invoiceTitle"
+          ><el-input v-model="form.invoiceTitle" placeholder="请输入发票抬头" disabled
+        /></el-form-item>
+        <el-form-item label="纳税人识别号" prop="taxNo"><el-input v-model="form.taxNo" placeholder="请输入纳税人识别号" disabled /></el-form-item>
+        <el-form-item label="开户银行" prop="bankName">
+          <el-select v-model="form.bankName" placeholder="请选择开户银行" clearable filterable style="width: 100%">
+            <el-option v-for="bank in bankList" :key="bank.id" :label="bank.bnName" :value="bank.bnName" />
+          </el-select>
+        </el-form-item>
         <el-form-item label="开户账户" prop="bankAccount"><el-input v-model="form.bankAccount" placeholder="请输入开户账户" /></el-form-item>
         <el-form-item label="地址" prop="address"><el-input v-model="form.address" placeholder="请输入地址" /></el-form-item>
         <el-form-item label="电话" prop="phone"><el-input v-model="form.phone" placeholder="请输入电话" /></el-form-item>
@@ -55,47 +69,93 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue'
-import { Search } from '@element-plus/icons-vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { getInvoiceList, addInvoice, updateInvoice, deleteInvoice } from '@/api/pc/enterprise'
+import { ref, reactive, onMounted } from 'vue';
+import { Phone, Search } from '@element-plus/icons-vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { getInvoiceList, addInvoice, updateInvoice, deleteInvoice } from '@/api/pc/enterprise';
+import { getBankList } from '@/api/pc/bank/bank';
 
-const dialogVisible = ref(false)
-const dialogTitle = ref('新增开票信息')
-const formRef = ref()
-const editingId = ref<number | null>(null)
-const total = ref(0)
+const dialogVisible = ref(false);
+const dialogTitle = ref('新增开票信息');
+const formRef = ref();
+const editingId = ref<number | null>(null);
+const total = ref(0);
 
-const queryParams = reactive({ pageNum: 1, pageSize: 10, keyword: '', searchType: 'accountName' })
-const form = reactive({ invoiceTitle: '', taxNo: '', bankName: '', bankAccount: '', address: '', phone: '', bankId: null, bankCode: '' })
+const queryParams = reactive({ pageNum: 1, pageSize: 10, keyword: '', searchType: 'accountName' });
+const form = reactive({ invoiceTitle: '', taxNo: '', bankName: '', bankAccount: '', address: '', phone: '', bankId: null, bankCode: '' });
 const rules = {
   invoiceTitle: [{ required: true, message: '请输入发票抬头', trigger: 'blur' }],
   taxNo: [{ required: true, message: '请输入纳税人识别号', trigger: 'blur' }],
   bankName: [{ required: true, message: '请输入开户银行', trigger: 'blur' }],
-  bankAccount: [{ required: true, message: '请输入开户账户', trigger: 'blur' }]
-}
+  bankAccount: [{ required: true, message: '请输入开户账户', trigger: 'blur' }],
+  phone: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }]
+};
 
-const tableData = ref([])
+const tableData = ref([]);
+const bankList = ref([]);
+
+// 加载银行列表
+const loadBankList = async () => {
+  try {
+    const res = await getBankList();
+    if (res.code === 200) {
+      bankList.value = res.rows || [];
+    }
+  } catch (error) {
+    console.error('加载银行列表失败:', error);
+  }
+};
 
 // 加载发票列表
 const loadInvoiceList = async () => {
   try {
-    const res = await getInvoiceList(queryParams)
+    const res = await getInvoiceList(queryParams);
     if (res.code === 200) {
-      tableData.value = res.rows || []
-      total.value = res.total || 0
+      tableData.value = res.rows || [];
+      total.value = res.total || 0;
     }
   } catch (error) {
-    console.error('加载发票列表失败:', error)
-    ElMessage.error('加载发票列表失败')
+    console.error('加载发票列表失败:', error);
+    ElMessage.error('加载发票列表失败');
   }
-}
+};
 
-onMounted(() => { loadInvoiceList() })
-const handleQuery = () => { loadInvoiceList() }
-const resetForm = () => { form.invoiceTitle = ''; form.taxNo = ''; form.bankName = ''; form.bankAccount = ''; form.address = ''; form.phone = ''; form.bankId = null; form.bankCode = ''; editingId.value = null }
-const handleAdd = () => { resetForm(); dialogTitle.value = '新增开票信息'; dialogVisible.value = true }
-const handleEdit = (row: any) => { editingId.value = row.id; form.invoiceTitle = row.invoiceTitle || ''; form.taxNo = row.taxId; form.bankName = row.bankName; form.bankAccount = row.bankAccount; form.address = row.address; form.phone = row.phone; form.bankId = row.bankId; form.bankCode = row.bankCode || ''; dialogTitle.value = '编辑开票信息'; dialogVisible.value = true }
+onMounted(() => {
+  loadInvoiceList();
+  loadBankList();
+});
+const handleQuery = () => {
+  loadInvoiceList();
+};
+const resetForm = () => {
+  form.invoiceTitle = '';
+  form.taxNo = '';
+  form.bankName = '';
+  form.bankAccount = '';
+  form.address = '';
+  form.phone = '';
+  form.bankId = null;
+  form.bankCode = '';
+  editingId.value = null;
+};
+const handleAdd = () => {
+  resetForm();
+  dialogTitle.value = '新增开票信息';
+  dialogVisible.value = true;
+};
+const handleEdit = (row: any) => {
+  editingId.value = row.id;
+  form.invoiceTitle = row.invoiceTitle || '';
+  form.taxNo = row.taxId;
+  form.bankName = row.bankName;
+  form.bankAccount = row.bankAccount;
+  form.address = row.address;
+  form.phone = row.phone;
+  form.bankId = row.bankId;
+  form.bankCode = row.bankCode || '';
+  dialogTitle.value = '编辑开票信息';
+  dialogVisible.value = true;
+};
 const handleSave = async () => {
   const valid = await formRef.value?.validate();
   if (!valid) return;
@@ -108,39 +168,77 @@ const handleSave = async () => {
       phone: form.phone,
       bankId: form.bankId || 0,
       bankCode: form.bankCode || 'DEFAULT'
-    }
-    console.log('发送的数据:', data)
+    };
+    console.log('发送的数据:', data);
     if (editingId.value) {
-      data.id = editingId.value
-      await updateInvoice(data)
+      data.id = editingId.value;
+      await updateInvoice(data);
     } else {
-      await addInvoice(data)
+      await addInvoice(data);
     }
-    ElMessage.success(editingId.value ? '修改成功' : '新增成功')
-    dialogVisible.value = false
-    loadInvoiceList()
+    ElMessage.success(editingId.value ? '修改成功' : '新增成功');
+    dialogVisible.value = false;
+    loadInvoiceList();
   } catch (error) {
-    console.error('保存失败:', error)
-    ElMessage.error('操作失败')
+    console.error('保存失败:', error);
+    ElMessage.error('操作失败');
   }
-}
+};
 const handleDelete = (row: any) => {
-  ElMessageBox.confirm('确定要删除该开票信息吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => {
-    try {
-      await deleteInvoice([row.id])
-      ElMessage.success('删除成功')
-      loadInvoiceList()
-    } catch (error) {
-      ElMessage.error('删除失败')
+  ElMessageBox.confirm('确定要删除该开票信息吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(
+    async () => {
+      try {
+        await deleteInvoice([row.id]);
+        ElMessage.success('删除成功');
+        loadInvoiceList();
+      } catch (error) {
+        ElMessage.error('删除失败');
+      }
     }
-  })
-}
+  );
+};
 </script>
 
 <style scoped lang="scss">
-.invoice-manage-container { padding: 20px; background: #fff; min-height: 100%; }
-.page-title { font-size: 16px; font-weight: bold; display: flex; align-items: center; gap: 8px; margin-bottom: 20px; }
-.title-bar { display: inline-block; width: 3px; height: 16px; background: #e60012; border-radius: 2px; }
-.search-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; .search-right { flex: 1; display: flex; justify-content: flex-end; } }
-.pagination-wrap { display: flex; justify-content: space-between; align-items: center; margin-top: 20px; .total-text { font-size: 14px; color: #666; } }
+.invoice-manage-container {
+  padding: 20px;
+  background: #fff;
+  min-height: 100%;
+}
+.page-title {
+  font-size: 16px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 20px;
+}
+.title-bar {
+  display: inline-block;
+  width: 3px;
+  height: 16px;
+  background: #e60012;
+  border-radius: 2px;
+}
+.search-bar {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-bottom: 20px;
+  .search-right {
+    flex: 1;
+    display: flex;
+    justify-content: flex-end;
+  }
+}
+.pagination-wrap {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 20px;
+  .total-text {
+    font-size: 14px;
+    color: #666;
+  }
+}
 </style>

+ 160 - 28
src/views/enterprise/purchaseHabit/index.vue

@@ -62,16 +62,14 @@
           <el-col :span="12">
             <el-form-item label="购买原装耗材">
               <el-radio-group v-model="form.buyOriginal">
-                <el-radio value="是">是</el-radio>
-                <el-radio value="否">否</el-radio>
+                <el-radio v-for="dict in sys_platform_yes_no" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="专人进行技术服务">
-              <el-radio-group v-model="form.techService">
-                <el-radio value="是">是</el-radio>
-                <el-radio value="否">否</el-radio>
+              <el-radio-group v-model="form.technologyService">
+                <el-radio v-for="dict in sys_platform_yes_no" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
@@ -104,7 +102,7 @@
               {{ item }}
             </div>
           </div>
-          <el-input v-model="form.otherWelfare" placeholder="其他福利" maxlength="50" show-word-limit class="other-input" />
+          <el-input v-model="form.otherScene" placeholder="其他福利" maxlength="50" show-word-limit class="other-input" />
         </el-form-item>
 
         <!-- 产品定制需求 -->
@@ -119,7 +117,7 @@
               {{ item }}
             </div>
           </div>
-          <el-input v-model="form.otherCustom" placeholder="其他需求" maxlength="50" show-word-limit class="other-input" />
+          <el-input v-model="form.otherCustomize" placeholder="其他需求" maxlength="50" show-word-limit class="other-input" />
         </el-form-item>
       </el-form>
 
@@ -133,33 +131,47 @@
 </template>
 
 <script setup lang="ts">
-import { reactive } from 'vue';
+import { reactive, getCurrentInstance, toRefs, onMounted, ComponentInternalInstance, computed } from 'vue';
 import { useRouter } from 'vue-router';
 import { ArrowLeft } from '@element-plus/icons-vue';
 import { ElMessage } from 'element-plus';
-import { getCustomerPurchaseHabitData } from '@/api/pc/enterprise/purchaseHabit';
+import { updatePurchaseHabit, getCustomerPurchaseHabitData } from '@/api/pc/enterprise/purchaseHabit';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { welfare_item, sys_platform_yes_no, product_types_choosing, purchase_item, product_customization, daily_print_volume } = toRefs<any>(
+  proxy?.useDict('welfare_item', 'sys_platform_yes_no', 'product_types_choosing', 'purchase_item', 'product_customization', 'daily_print_volume')
+);
 
 const router = useRouter();
 
-const productTypeOptions = ['经济适用', '性价比高', '大牌商品'];
-const printVolumeOptions = ['100-500张', '500-1000张', '1000-3000张', '3000张以上'];
-const categoryOptions = ['办公耗材', '办公设备', '数码设备', '办公日用', '办公家具', '清洁用品', '劳保用品', '食品饮料'];
-const welfareOptions = ['新年福利', '中秋福利', '端午福利', '生日福利', '员工体检', '团建活动', '节日礼品', '年终奖品'];
-const customOptions = ['服装定制', '礼品定制', '包装定制', '印刷定制', 'LOGO定制', '文创定制', '工艺品定制', '其他定制'];
+// 根据字典数据生成选项
+const productTypeOptions = computed(() => product_types_choosing.value?.map((item: any) => item.label) || []);
+const printVolumeOptions = computed(() => daily_print_volume.value?.map((item: any) => item.label) || []);
+const categoryOptions = computed(() => purchase_item.value?.map((item: any) => item.label) || []);
+const welfareOptions = computed(() => welfare_item.value?.map((item: any) => item.label) || []);
+const customOptions = computed(() => product_customization.value?.map((item: any) => item.label) || []);
 
 const form = reactive({
+  id: undefined,
+  customerId: undefined,
+  customerNo: '',
+  permanentOfficer: '',
   monthlyAmount: '',
   yearlyAmount: '',
-  productTypes: ['经济适用'] as string[],
-  printVolume: '100-500张',
-  buyOriginal: '是',
-  techService: '是',
-  categories: ['办公耗材'] as string[],
+  productTypes: [] as string[],
+  printVolume: '',
+  printAmount: '',
+  buyOriginal: '',
+  technologyService: '',
+  categories: [] as string[],
   otherCategory: '',
-  welfares: ['新年福利'] as string[],
-  otherWelfare: '',
-  customs: ['服装定制'] as string[],
-  otherCustom: ''
+  welfares: [] as string[],
+  customs: [] as string[],
+  adaptScenes: [] as string[],
+  otherScene: '',
+  otherCustomize: '',
+  choiceModel: '',
+  remark: ''
 });
 
 const toggleTag = (arr: string[], item: string) => {
@@ -170,14 +182,134 @@ const toggleTag = (arr: string[], item: string) => {
 const handleBack = () => {
   router.push('/enterprise/companyInfo');
 };
-const handleSave = () => {
-  ElMessage.success('保存成功');
-  handleBack();
+const handleSave = async () => {
+  try {
+    // 将表单数据映射为接口所需格式
+    const submitData = {
+      id: form.id,
+      customerId: form.customerId,
+      customerNo: form.customerNo,
+      monthPurchase: form.monthlyAmount ? parseFloat(form.monthlyAmount) : undefined,
+      yearPurchase: form.yearlyAmount ? parseFloat(form.yearlyAmount) : undefined,
+      permanentOfficer: form.permanentOfficer,
+      choiceModel: form.productTypes.length > 0 ? product_types_choosing.value?.find((i: any) => i.label === form.productTypes[0])?.value || '' : '',
+      printAmount: form.printVolume ? daily_print_volume.value?.find((i: any) => i.label === form.printVolume)?.value || '' : '',
+      buyOriginal: form.buyOriginal,
+      technologyService: form.technologyService,
+      purchaseCategory:
+        form.categories.length > 0
+          ? form.categories
+              .map((label) => {
+                const item = purchase_item.value?.find((i: any) => i.label === label);
+                return item ? item.value : '';
+              })
+              .filter(Boolean)
+              .join(',')
+          : undefined,
+      otherCategory: form.otherCategory || undefined,
+      adaptScene:
+        form.welfares.length > 0
+          ? form.welfares
+              .map((label) => {
+                const item = welfare_item.value?.find((i: any) => i.label === label);
+                return item ? item.value : '';
+              })
+              .filter(Boolean)
+              .join(',')
+          : undefined,
+      otherScene: form.otherScene || undefined,
+      customizeDemand:
+        form.customs.length > 0
+          ? form.customs
+              .map((label) => {
+                const item = product_customization.value?.find((i: any) => i.label === label);
+                return item ? item.value : '';
+              })
+              .filter(Boolean)
+              .join(',')
+          : undefined,
+      otherCustomize: form.otherCustomize || undefined,
+      remark: form.remark || undefined
+    };
+
+    await updatePurchaseHabit(submitData);
+    ElMessage.success('保存成功');
+    handleBack();
+  } catch (error) {
+    ElMessage.error('保存失败');
+  }
 };
 
 const getPurchaseHabitData = async () => {
-  const res = await getCustomerPurchaseHabitData();
-  Object.assign(form, res);
+  try {
+    const res = await getCustomerPurchaseHabitData();
+    if (res.code === 200 && res.data) {
+      const data = res.data;
+
+      // 映射数据到表单
+      form.id = data.id;
+      form.customerId = data.customerId;
+      form.customerNo = data.customerNo || '';
+      form.permanentOfficer = data.permanentOfficer || '';
+      form.monthlyAmount = data.monthPurchase || '';
+      form.yearlyAmount = data.yearPurchase || '';
+
+      // 处理产品选型(单选)
+      form.productTypes = data.choiceModel ? [product_types_choosing.value?.find((i: any) => i.value === data.choiceModel)?.label || ''] : [];
+
+      // 处理日常打印量(单选)
+      form.printVolume = data.printAmount ? daily_print_volume.value?.find((i: any) => i.value === data.printAmount)?.label || '' : '';
+
+      form.categories = data.purchaseCategory
+        ? data.purchaseCategory
+            .split(',')
+            .map((id: string) => {
+              const item = purchase_item.value?.find((i: any) => i.value === id);
+              return item ? item.label : '';
+            })
+            .filter(Boolean)
+        : [];
+
+      form.welfares = data.adaptScene
+        ? data.adaptScene
+            .split(',')
+            .map((id: string) => {
+              const item = welfare_item.value?.find((i: any) => i.value === id);
+              return item ? item.label : '';
+            })
+            .filter(Boolean)
+        : [];
+
+      form.customs = data.customizeDemand
+        ? data.customizeDemand
+            .split(',')
+            .map((id: string) => {
+              const item = product_customization.value?.find((i: any) => i.value === id);
+              return item ? item.label : '';
+            })
+            .filter(Boolean)
+        : [];
+
+      // 处理单选字段
+      form.printAmount = data.printAmount || '';
+
+      form.buyOriginal = data.buyOriginal || '';
+
+      form.technologyService = data.technologyService;
+
+      form.choiceModel = data.choiceModel || '';
+
+      // 其他字段
+      form.otherCategory = data.otherCategory || '';
+      form.otherCustomize = data.otherCustomize || '';
+      form.otherScene = data.otherScene || '';
+      form.otherCustomize = data.otherCustomize || '';
+      form.remark = data.remark || '';
+    }
+  } catch (error) {
+    console.error('获取采购习惯数据失败:', error);
+    ElMessage.error('获取数据失败');
+  }
 };
 
 onMounted(() => {

+ 53 - 63
src/views/i/index.vue

@@ -25,19 +25,12 @@
       <div class="form-row">
         <el-form-item label="性别" prop="gender">
           <el-select v-model="formData.gender" placeholder="请选择" style="width: 100%">
-            <el-option label="男" value="0" />
-            <el-option label="女" value="1" />
-            <el-option label="未知" value="2" />
+            <el-option v-for="dict in sys_user_sex" :key="dict.value" :label="dict.label" :value="dict.value" />
           </el-select>
         </el-form-item>
         <el-form-item label="角色" prop="role">
           <el-select v-model="formData.role" placeholder="请选择" style="width: 100%">
-            <el-option
-              v-for="role in roleList"
-              :key="role.roleId"
-              :label="role.roleName"
-              :value="role.roleId.toString()"
-            />
+            <el-option v-for="role in roleList" :key="role.roleId" :label="role.roleName" :value="role.roleId.toString()" />
           </el-select>
         </el-form-item>
       </div>
@@ -57,12 +50,7 @@
         </el-form-item>
         <el-form-item label="所属部门" prop="department">
           <el-select v-model="formData.department" placeholder="请选择" style="width: 100%">
-            <el-option
-              v-for="dept in deptList"
-              :key="dept.deptId"
-              :label="dept.deptName"
-              :value="dept.deptId.toString()"
-            />
+            <el-option v-for="dept in deptList" :key="dept.deptId" :label="dept.deptName" :value="dept.deptId.toString()" />
           </el-select>
         </el-form-item>
       </div>
@@ -93,14 +81,16 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue'
-import { ElMessage } from 'element-plus'
-import { PageTitle } from '@/components'
-import { getCurrentUserInfo, updateContact, getDeptList, getRoleList } from '@/api/pc/organization'
+import { ref, reactive, onMounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import { PageTitle } from '@/components';
+import { getCurrentUserInfo, updateContact, getDeptList, getDeptTree, getRoleList } from '@/api/pc/organization';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_user_sex } = toRefs<any>(proxy?.useDict('sys_user_sex'));
 
-const formRef = ref()
-const deptList = ref<any[]>([])
-const roleList = ref<any[]>([])
+const formRef = ref();
+const deptList = ref<any[]>([]);
+const roleList = ref<any[]>([]);
 
 const formData = reactive({
   id: 0,
@@ -117,43 +107,43 @@ const formData = reactive({
   subDepartment: '',
   address: '',
   remark: ''
-})
+});
 
 const rules = {
   realName: [{ required: true, message: '请输入真实姓名', trigger: 'blur' }],
   phone: [{ required: true, message: '请输入手机号码', trigger: 'blur' }]
-}
+};
 
 // 获取当前用户信息
 const loadUserInfo = async () => {
   try {
-    const res = await getCurrentUserInfo()
+    const res = await getCurrentUserInfo();
     if (res.code === 200 && res.data) {
       // 映射后端字段到前端字段
-      formData.id = res.data.id
-      formData.userId = res.data.id?.toString() || ''
-      formData.phone = res.data.phone || ''
-      formData.realName = res.data.contactName || ''
-      formData.officePhone = res.data.officePhone || ''
-      formData.gender = res.data.gender || ''
-      formData.role = res.data.roleId?.toString() || ''
-      formData.birthday = res.data.birthday || ''
-      formData.wechat = res.data.customLoginName || ''  // 微信号映射到customLoginName
-      formData.email = res.data.email || ''
-      formData.department = res.data.deptId?.toString() || ''  // 部门ID
-      formData.subDepartment = res.data.provincialCityCounty || ''  // 省市区
-      formData.address = res.data.addressDetail || ''
-      formData.remark = res.data.remark || ''
+      formData.id = res.data.id;
+      formData.userId = res.data.id?.toString() || '';
+      formData.phone = res.data.phone || '';
+      formData.realName = res.data.contactName || '';
+      formData.officePhone = res.data.officePhone || '';
+      formData.gender = res.data.gender || '';
+      formData.role = res.data.roleId?.toString() || '';
+      formData.birthday = res.data.birthday || '';
+      formData.wechat = res.data.customLoginName || ''; // 微信号映射到customLoginName
+      formData.email = res.data.email || '';
+      formData.department = res.data.deptId?.toString() || ''; // 部门ID
+      formData.subDepartment = res.data.provincialCityCounty || ''; // 省市区
+      formData.address = res.data.addressDetail || '';
+      formData.remark = res.data.remark || '';
     }
   } catch (error) {
-    console.error('获取用户信息失败:', error)
-    ElMessage.error('获取用户信息失败')
+    console.error('获取用户信息失败:', error);
+    ElMessage.error('获取用户信息失败');
   }
-}
+};
 
 const handleSave = async () => {
-  const valid = await formRef.value?.validate()
-  if (!valid) return
+  const valid = await formRef.value?.validate();
+  if (!valid) return;
 
   try {
     // 映射前端字段到后端字段
@@ -171,48 +161,48 @@ const handleSave = async () => {
       provincialCityCounty: formData.subDepartment,
       addressDetail: formData.address,
       remark: formData.remark
-    }
+    };
 
-    await updateContact(data)
-    ElMessage.success('保存成功')
+    await updateContact(data);
+    ElMessage.success('保存成功');
   } catch (error) {
-    console.error('保存失败:', error)
-    ElMessage.error('保存失败')
+    console.error('保存失败:', error);
+    ElMessage.error('保存失败');
   }
-}
+};
 
 // 加载部门列表
 const loadDeptList = async () => {
   try {
-    const res = await getDeptList()
-    console.log('部门列表响应:', res)
+    const res = await getDeptTree();
+    console.log('部门列表响应:', res);
     if (res.code === 200 && res.data) {
-      deptList.value = res.data
-      console.log('部门列表加载成功:', deptList.value)
+      deptList.value = res.data;
+      console.log('部门列表加载成功:', deptList.value);
     }
   } catch (error) {
-    console.error('获取部门列表失败:', error)
+    console.error('获取部门列表失败:', error);
   }
-}
+};
 
 // 加载角色列表
 const loadRoleList = async () => {
   try {
-    const res = await getRoleList()
+    const res = await getRoleList();
     if (res.code === 200 && res.data) {
-      roleList.value = res.data
+      roleList.value = res.data;
     }
   } catch (error) {
-    console.error('获取角色列表失败:', error)
+    console.error('获取角色列表失败:', error);
   }
-}
+};
 
 // 页面加载时获取用户信息、部门列表和角色列表
 onMounted(async () => {
   // 先加载部门和角色列表,再加载用户信息
-  await Promise.all([loadDeptList(), loadRoleList()])
-  await loadUserInfo()
-})
+  await Promise.all([loadDeptList(), loadRoleList()]);
+  await loadUserInfo();
+});
 </script>
 
 <style scoped lang="scss">

+ 380 - 117
src/views/order/afterSale/index.vue

@@ -14,16 +14,30 @@
 
     <template v-if="activeMainTab !== 'complaint'">
       <div class="status-tabs">
-        <div v-for="tab in statusTabs" :key="tab.key" :class="['status-tab', { active: activeStatusTab === tab.key }]" @click="handleStatusTabChange(tab.key)">
+        <div
+          v-for="tab in statusTabs"
+          :key="tab.key"
+          :class="['status-tab', { active: activeStatusTab === tab.key }]"
+          @click="handleStatusTabChange(tab.key)"
+        >
           {{ tab.label }}
         </div>
       </div>
 
       <div class="search-bar">
         <el-input v-model="queryParams.keyword" placeholder="搜索" style="width: 150px" clearable>
-          <template #prefix><el-icon><Search /></el-icon></template>
+          <template #prefix
+            ><el-icon><Search /></el-icon
+          ></template>
         </el-input>
-        <el-date-picker v-model="queryParams.dateRange" type="daterange" range-separator="—" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 240px" />
+        <el-date-picker
+          v-model="queryParams.dateRange"
+          type="daterange"
+          range-separator="—"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          style="width: 240px"
+        />
       </div>
 
       <div class="filter-bar">
@@ -55,7 +69,10 @@
             <div class="col-product">
               <div class="product-image">
                 <el-image :src="item.productImage" fit="contain">
-                  <template #error><div class="image-placeholder"><el-icon :size="30" color="#ccc"><Picture /></el-icon></div></template>
+                  <template #error
+                    ><div class="image-placeholder">
+                      <el-icon :size="30" color="#ccc"><Picture /></el-icon></div
+                  ></template>
                 </el-image>
               </div>
               <div class="product-info">
@@ -65,7 +82,9 @@
             </div>
             <div class="col-time">{{ item.applyTime }}</div>
             <div class="col-type">{{ item.serviceType }}</div>
-            <div class="col-status"><span :class="['status-text', getStatusClass(item.status)]">{{ item.statusText }}</span></div>
+            <div class="col-status">
+              <span :class="['status-text', getStatusClass(item.status)]">{{ item.statusText }}</span>
+            </div>
             <div class="col-action"><el-button type="primary" link size="small" @click="handleViewDetail(item)">查看详情</el-button></div>
           </div>
         </div>
@@ -75,7 +94,12 @@
 
     <template v-else>
       <div class="status-tabs">
-        <div v-for="tab in complaintStatusTabs" :key="tab.key" :class="['status-tab', { active: activeStatusTab === tab.key }]" @click="activeStatusTab = tab.key">
+        <div
+          v-for="tab in complaintStatusTabs"
+          :key="tab.key"
+          :class="['status-tab', { active: activeStatusTab === tab.key }]"
+          @click="activeStatusTab = tab.key"
+        >
           {{ tab.label }}
         </div>
       </div>
@@ -83,7 +107,9 @@
       <div class="search-filter-bar">
         <div class="left">
           <el-input v-model="queryParams.keyword" placeholder="搜索" style="width: 150px" clearable>
-            <template #prefix><el-icon><Search /></el-icon></template>
+            <template #prefix
+              ><el-icon><Search /></el-icon
+            ></template>
           </el-input>
           <span class="filter-label">处理结果</span>
           <el-select v-model="queryParams.handleResult" placeholder="请选择" style="width: 100px" clearable>
@@ -92,8 +118,7 @@
           </el-select>
           <span class="filter-label">反馈类型</span>
           <el-select v-model="queryParams.feedbackType" placeholder="请选择" style="width: 100px" clearable>
-            <el-option label="投诉" value="complaint" />
-            <el-option label="建议" value="suggestion" />
+            <el-option v-for="dict in complaints_suggestion_type" :key="dict.value" :label="dict.label" :value="dict.value" />
           </el-select>
         </div>
         <div class="right">
@@ -103,16 +128,25 @@
 
       <el-table :data="complaintList" border style="width: 100%">
         <el-table-column type="index" label="序号" width="70" align="center" />
-        <el-table-column prop="submitTime" label="提交时间" min-width="150" align="center" />
-        <el-table-column prop="submitter" label="提交人" min-width="100" align="center" />
+        <el-table-column prop="createTime" label="提交时间" min-width="150" align="center">
+          <template #default="{ row }"
+            ><span>{{ parseTime(row.createTime, '{y}-{m}-{d}') }}</span></template
+          >
+        </el-table-column>
+        <el-table-column prop="createName" label="提交人" min-width="100" align="center" />
         <el-table-column prop="feedbackType" label="反馈类型" min-width="100" align="center">
-          <template #default="{ row }"><span>{{ row.feedbackType || '-' }}</span></template>
+          <template #default="{ row }">
+            {{ getDictLabel(complaints_suggestion_type, row.feedbackType) }}
+          </template>
         </el-table-column>
-        <el-table-column prop="handleTime" label="处理时间" min-width="150" align="center" />
-        <el-table-column prop="handler" label="处理人员" min-width="100" align="center" />
-        <el-table-column prop="handleResult" label="处理结果" min-width="100" align="center">
-          <template #default="{ row }"><span :style="{ color: row.handleResult === '未处理' ? '#e60012' : '#333' }">{{ row.handleResult }}</span></template>
+
+        <el-table-column prop="processTime" label="处理时间" min-width="150" align="center">
+          <template #default="{ row }"
+            ><span>{{ parseTime(row.processTime, '{y}-{m}-{d}') }}</span></template
+          >
         </el-table-column>
+        <el-table-column prop="processStaff" label="处理人员" min-width="100" align="center" />
+        <el-table-column prop="processResult" label="处理结果" min-width="100" align="center" />
         <el-table-column label="操作" min-width="120" align="center">
           <template #default="{ row }">
             <el-button type="primary" link size="small" @click="handleEditComplaint(row)">修改</el-button>
@@ -127,12 +161,11 @@
       <el-form ref="complaintFormRef" :model="complaintForm" :rules="complaintRules" label-width="80px">
         <el-form-item label="反馈类型" prop="feedbackType">
           <el-select v-model="complaintForm.feedbackType" placeholder="请选择" style="width: 100%">
-            <el-option label="投诉" value="投诉" />
-            <el-option label="建议" value="建议" />
+            <el-option v-for="dict in complaints_suggestion_type" :key="dict.value" :label="dict.label" :value="dict.value" />
           </el-select>
         </el-form-item>
-        <el-form-item label="反馈内容" prop="content">
-          <el-input v-model="complaintForm.content" type="textarea" :rows="4" placeholder="请输入反馈内容" />
+        <el-form-item label="反馈内容" prop="feedbackContent">
+          <el-input v-model="complaintForm.feedbackContent" type="textarea" :rows="4" placeholder="请输入反馈内容" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -144,27 +177,36 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted } from 'vue'
-import { useRouter } from 'vue-router'
-import { Search, RefreshRight, Tools, ChatLineSquare, Picture } from '@element-plus/icons-vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { getOrderReturnList, getOrderReturnInfo, addOrderReturn, updateOrderReturn, deleteOrderReturn } from '@/api/pc/enterprise/orderReturn'
-import type { OrderReturn } from '@/api/pc/enterprise/orderReturnTypes'
+import { ref, reactive, computed, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { Search, RefreshRight, Tools, ChatLineSquare, Picture } from '@element-plus/icons-vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { getOrderReturnList, getOrderReturnInfo, addOrderReturn, updateOrderReturn, deleteOrderReturn } from '@/api/pc/enterprise/orderReturn';
+import type { OrderReturn } from '@/api/pc/enterprise/orderReturnTypes';
+import {
+  getComplaintsSuggestionsList,
+  addComplaintsSuggestions,
+  updateComplaintsSuggestions,
+  deleteComplaintsSuggestions
+} from '@/api/pc/valueAdded';
+import { lo } from 'element-plus/es/locale/index.mjs';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { complaints_suggestion_type } = toRefs<any>(proxy?.useDict('complaints_suggestion_type'));
 
-const router = useRouter()
-const activeMainTab = ref('return')
-const activeStatusTab = ref('all')
-const complaintDialogVisible = ref(false)
-const complaintDialogTitle = ref('新增投诉与建议')
-const complaintFormRef = ref()
-const currentComplaint = ref<any>(null)
-const loading = ref(false)
+const router = useRouter();
+const activeMainTab = ref('return');
+const activeStatusTab = ref('all');
+const complaintDialogVisible = ref(false);
+const complaintDialogTitle = ref('新增投诉与建议');
+const complaintFormRef = ref();
+const currentComplaint = ref<any>(null);
+const loading = ref(false);
 
 const mainTabs = [
   { key: 'return', label: '退换货', icon: RefreshRight },
   { key: 'repair', label: '返修/维修', icon: Tools },
   { key: 'complaint', label: '投诉与建议', icon: ChatLineSquare }
-]
+];
 
 const statusTabs = [
   { key: 'all', label: '全部订单' },
@@ -173,7 +215,7 @@ const statusTabs = [
   { key: 'rejected', label: '已拒绝' },
   { key: 'completed', label: '已完成' },
   { key: 'cancelled', label: '已取消' }
-]
+];
 
 const complaintStatusTabs = [
   { key: 'all', label: '全部订单' },
@@ -182,26 +224,26 @@ const complaintStatusTabs = [
   { key: 'rejected', label: '已拒绝' },
   { key: 'completed', label: '已完成' },
   { key: 'cancelled', label: '已取消' }
-]
+];
 
-const queryParams = reactive({ keyword: '', dateRange: null, status: '', handleResult: '', feedbackType: '' })
-const complaintForm = reactive({ feedbackType: '', content: '' })
+const queryParams = reactive({ keyword: '', dateRange: null, status: '', handleResult: '', feedbackType: '' });
+const complaintForm = reactive({ id: undefined, feedbackType: '', feedbackContent: '' });
 const complaintRules = {
   feedbackType: [{ required: true, message: '请选择反馈类型', trigger: 'change' }],
-  content: [{ required: true, message: '请输入反馈内容', trigger: 'blur' }]
-}
+  feedbackContent: [{ required: true, message: '请输入反馈内容', trigger: 'blur' }]
+};
 
 // 售后数据列表
-const allAfterSaleData = ref<any[]>([])
+const allAfterSaleData = ref<any[]>([]);
 
 // 加载售后数据
 const loadAfterSaleData = async () => {
   try {
-    loading.value = true
+    loading.value = true;
     const res = await getOrderReturnList({
       returnStatus: activeStatusTab.value === 'all' ? '' : activeStatusTab.value,
       serviceType: '0'
-    })
+    });
 
     if (res.code === 200 && res.rows) {
       // 将后端数据转换为前端展示格式
@@ -217,15 +259,21 @@ const loadAfterSaleData = async () => {
         status: item.returnStatus,
         statusText: getStatusText(item.returnStatus),
         type: item.serviceType === '0' ? 'return' : 'repair'
-      }))
+      }));
     }
   } catch (error) {
-    console.error('加载售后数据失败:', error)
-    ElMessage.error('加载售后数据失败')
+    console.error('加载售后数据失败:', error);
+    ElMessage.error('加载售后数据失败');
   } finally {
-    loading.value = false
+    loading.value = false;
   }
-}
+};
+
+const getDictLabel = (dictOptions: any[], value: string) => {
+  if (!dictOptions || !value) return value;
+  const dict = dictOptions.find((item) => item.value === value);
+  return dict ? dict.label : value;
+};
 
 // 获取状态文本
 const getStatusText = (status?: string) => {
@@ -235,103 +283,318 @@ const getStatusText = (status?: string) => {
     'rejected': '已拒绝',
     'completed': '已完成',
     'cancelled': '已取消'
-  }
-  return statusMap[status || ''] || status || '未知'
-}
+  };
+  return statusMap[status || ''] || status || '未知';
+};
 
-const allComplaintData = ref([
-  { id: 1, submitTime: '2025-11-30 22:16', submitter: '李豫卓', feedbackType: '', handleTime: '2025-12-10 23:04', handler: '李豫卓', handleResult: '已处理' },
-  { id: 2, submitTime: '2025-11-17 13:22', submitter: '冯启彬', feedbackType: '', handleTime: '2025-11-22 09:29', handler: '孙婉茹', handleResult: '未处理' },
-  { id: 3, submitTime: '2025-11-30 12:23', submitter: '冯云', feedbackType: '', handleTime: '2025-11-27 07:40', handler: '冯思玖', handleResult: '已处理' },
-  { id: 4, submitTime: '2025-12-07 04:15', submitter: '李世海', feedbackType: '', handleTime: '2025-11-24 13:43', handler: '郑文锦', handleResult: '已处理' },
-  { id: 5, submitTime: '2025-12-10 07:02', submitter: '孙思达', feedbackType: '', handleTime: '2025-11-13 01:24', handler: '周璟', handleResult: '已处理' }
-])
+const allComplaintData = ref([]);
 
 const afterSaleList = computed(() => {
-  let list = allAfterSaleData.value.filter(item => item.type === activeMainTab.value)
-  if (activeStatusTab.value !== 'all') list = list.filter(item => item.status === activeStatusTab.value)
-  return list
-})
+  let list = allAfterSaleData.value.filter((item) => item.type === activeMainTab.value);
+  if (activeStatusTab.value !== 'all') list = list.filter((item) => item.status === activeStatusTab.value);
+  return list;
+});
 
-const complaintList = computed(() => allComplaintData.value)
+const complaintList = ref([]);
 
 const handleMainTabChange = (key: string) => {
-  activeMainTab.value = key
-  activeStatusTab.value = 'all'
+  activeMainTab.value = key;
+  activeStatusTab.value = 'all';
   if (key !== 'complaint') {
-    loadAfterSaleData()
+    loadAfterSaleData();
+  } else {
+    loadComplaintData();
   }
-}
+};
 
 // 状态切换时刷新数据
 const handleStatusTabChange = (key: string) => {
-  activeStatusTab.value = key
-  loadAfterSaleData()
-}
+  activeStatusTab.value = key;
+  loadAfterSaleData();
+};
 
 // 页面加载时获取数据
 onMounted(() => {
-  loadAfterSaleData()
-})
+  loadAfterSaleData();
+});
 const getStatusClass = (status: string) => {
-  const map: Record<string, string> = { applying: 'warning', returned: 'success', rejected: 'danger', completed: 'success', cancelled: 'info' }
-  return map[status] || 'info'
-}
-const handleViewDetail = (item: any) => { console.log('查看详情', item) }
-const handleAddComplaint = () => { currentComplaint.value = null; complaintDialogTitle.value = '新增投诉与建议'; complaintForm.feedbackType = ''; complaintForm.content = ''; complaintDialogVisible.value = true }
-const handleEditComplaint = (row: any) => { currentComplaint.value = row; complaintDialogTitle.value = '修改投诉与建议'; complaintForm.feedbackType = row.feedbackType; complaintForm.content = row.content || ''; complaintDialogVisible.value = true }
+  const map: Record<string, string> = { applying: 'warning', returned: 'success', rejected: 'danger', completed: 'success', cancelled: 'info' };
+  return map[status] || 'info';
+};
+const handleViewDetail = (item: any) => {
+  console.log('查看详情', item);
+};
+const handleAddComplaint = () => {
+  currentComplaint.value = null;
+  complaintDialogTitle.value = '新增投诉与建议';
+  complaintForm.feedbackType = '';
+  complaintForm.feedbackContent = '';
+  complaintDialogVisible.value = true;
+};
+const handleEditComplaint = (row: any) => {
+  currentComplaint.value = row;
+  complaintDialogTitle.value = '修改投诉与建议';
+  complaintForm.id = row.id;
+  complaintForm.feedbackType = row.feedbackType;
+  complaintForm.feedbackContent = row.feedbackContent || '';
+  complaintDialogVisible.value = true;
+};
 const handleDeleteComplaint = (row: any) => {
-  ElMessageBox.confirm('确定要删除该记录吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
-    const index = allComplaintData.value.findIndex(item => item.id === row.id)
-    if (index > -1) allComplaintData.value.splice(index, 1)
-    ElMessage.success('删除成功')
-  }).catch(() => {})
-}
+  ElMessageBox.confirm('确定要删除该记录吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => {
+    try {
+      await deleteComplaintsSuggestions([row.id]);
+      ElMessage.success('删除成功');
+      loadComplaintData();
+    } catch (error) {
+      ElMessage.error('删除失败');
+    }
+  });
+};
+
 const handleSubmitComplaint = async () => {
-  const valid = await complaintFormRef.value?.validate()
-  if (!valid) return
-  if (currentComplaint.value) { currentComplaint.value.feedbackType = complaintForm.feedbackType; currentComplaint.value.content = complaintForm.content; ElMessage.success('修改成功') }
-  else { allComplaintData.value.unshift({ id: Date.now(), submitTime: new Date().toLocaleString(), submitter: '当前用户', feedbackType: complaintForm.feedbackType, handleTime: '', handler: '', handleResult: '未处理' }); ElMessage.success('提交成功') }
-  complaintDialogVisible.value = false
-}
+  const valid = await complaintFormRef.value?.validate();
+  if (!valid) return;
+  if (complaintForm.id) {
+    await updateComplaintsSuggestions(complaintForm);
+    ElMessage.success('修改成功');
+  } else {
+    await addComplaintsSuggestions(complaintForm);
+    ElMessage.success('提交成功');
+  }
+  loadComplaintData();
+  complaintDialogVisible.value = false;
+};
+
+const loadComplaintData = async () => {
+  const params: any = {};
+  const res = await getComplaintsSuggestionsList(params);
+  if (res.code === 200 && res.rows) {
+    complaintList.value = res.rows;
+  }
+};
 </script>
 
 <style scoped lang="scss">
-.after-sale-container { padding: 20px; background: #fff; min-height: 100%; }
-.page-title { font-size: 16px; font-weight: bold; display: flex; align-items: center; gap: 8px; margin-bottom: 20px; }
-.title-bar { display: inline-block; width: 3px; height: 16px; background: #e60012; border-radius: 2px; }
-.main-tabs { display: flex; gap: 30px; border-bottom: 1px solid #eee; margin-bottom: 15px;
-  .tab-item { display: flex; align-items: center; gap: 5px; padding: 12px 0; cursor: pointer; color: #666; font-size: 14px; border-bottom: 2px solid transparent; margin-bottom: -1px;
-    &:hover, &.active { color: #333; } &.active { border-bottom-color: #e60012; }
+.after-sale-container {
+  padding: 20px;
+  background: #fff;
+  min-height: 100%;
+}
+.page-title {
+  font-size: 16px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 20px;
+}
+.title-bar {
+  display: inline-block;
+  width: 3px;
+  height: 16px;
+  background: #e60012;
+  border-radius: 2px;
+}
+.main-tabs {
+  display: flex;
+  gap: 30px;
+  border-bottom: 1px solid #eee;
+  margin-bottom: 15px;
+  .tab-item {
+    display: flex;
+    align-items: center;
+    gap: 5px;
+    padding: 12px 0;
+    cursor: pointer;
+    color: #666;
+    font-size: 14px;
+    border-bottom: 2px solid transparent;
+    margin-bottom: -1px;
+    &:hover,
+    &.active {
+      color: #333;
+    }
+    &.active {
+      border-bottom-color: #e60012;
+    }
   }
 }
-.status-tabs { display: flex; gap: 10px; margin-bottom: 15px;
-  .status-tab { padding: 6px 18px; border-radius: 4px; cursor: pointer; font-size: 13px; color: #666; background: #f5f5f5; transition: all 0.2s;
-    &:hover { color: #e60012; } &.active { background: #e60012; color: #fff; }
+.status-tabs {
+  display: flex;
+  gap: 10px;
+  margin-bottom: 15px;
+  .status-tab {
+    padding: 6px 18px;
+    border-radius: 4px;
+    cursor: pointer;
+    font-size: 13px;
+    color: #666;
+    background: #f5f5f5;
+    transition: all 0.2s;
+    &:hover {
+      color: #e60012;
+    }
+    &.active {
+      background: #e60012;
+      color: #fff;
+    }
   }
 }
-.search-bar { display: flex; align-items: center; gap: 15px; margin-bottom: 15px; }
-.filter-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; .filter-label { font-size: 14px; color: #666; } }
-.search-filter-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;
-  .left { display: flex; align-items: center; gap: 10px; .filter-label { font-size: 14px; color: #666; margin-left: 10px; } }
+.search-bar {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  margin-bottom: 15px;
 }
-.table-header { display: flex; align-items: center; padding: 12px 15px; background: #f9f9f9; border: 1px solid #eee; border-bottom: none; font-size: 13px; color: #666;
-  .col-product { flex: 1; } .col-time { width: 150px; text-align: center; } .col-type { width: 100px; text-align: center; } .col-status { width: 100px; text-align: center; } .col-action { width: 100px; text-align: center; }
+.filter-bar {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-bottom: 20px;
+  .filter-label {
+    font-size: 14px;
+    color: #666;
+  }
+}
+.search-filter-bar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+  .left {
+    display: flex;
+    align-items: center;
+    gap: 10px;
+    .filter-label {
+      font-size: 14px;
+      color: #666;
+      margin-left: 10px;
+    }
+  }
+}
+.table-header {
+  display: flex;
+  align-items: center;
+  padding: 12px 15px;
+  background: #f9f9f9;
+  border: 1px solid #eee;
+  border-bottom: none;
+  font-size: 13px;
+  color: #666;
+  .col-product {
+    flex: 1;
+  }
+  .col-time {
+    width: 150px;
+    text-align: center;
+  }
+  .col-type {
+    width: 100px;
+    text-align: center;
+  }
+  .col-status {
+    width: 100px;
+    text-align: center;
+  }
+  .col-action {
+    width: 100px;
+    text-align: center;
+  }
 }
 .after-sale-list {
-  .after-sale-card { border: 1px solid #eee; margin-bottom: -1px;
-    .card-header { display: flex; align-items: center; gap: 20px; padding: 10px 15px; background: #fafafa; border-bottom: 1px solid #eee; font-size: 13px; color: #333; }
-    .card-content { display: flex; align-items: center; padding: 15px;
-      .col-product { flex: 1; display: flex; align-items: center; gap: 12px;
-        .product-image { width: 70px; height: 70px; background: #f5f5f5; border-radius: 4px; overflow: hidden; flex-shrink: 0;
-          .el-image { width: 100%; height: 100%; } .image-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
+  .after-sale-card {
+    border: 1px solid #eee;
+    margin-bottom: -1px;
+    .card-header {
+      display: flex;
+      align-items: center;
+      gap: 20px;
+      padding: 10px 15px;
+      background: #fafafa;
+      border-bottom: 1px solid #eee;
+      font-size: 13px;
+      color: #333;
+    }
+    .card-content {
+      display: flex;
+      align-items: center;
+      padding: 15px;
+      .col-product {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        gap: 12px;
+        .product-image {
+          width: 70px;
+          height: 70px;
+          background: #f5f5f5;
+          border-radius: 4px;
+          overflow: hidden;
+          flex-shrink: 0;
+          .el-image {
+            width: 100%;
+            height: 100%;
+          }
+          .image-placeholder {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+          }
+        }
+        .product-info {
+          .product-name {
+            font-size: 13px;
+            color: #333;
+            line-height: 1.4;
+            margin-bottom: 5px;
+            display: -webkit-box;
+            -webkit-line-clamp: 2;
+            -webkit-box-orient: vertical;
+            overflow: hidden;
+          }
+          .product-quantity {
+            font-size: 12px;
+            color: #666;
+          }
         }
-        .product-info { .product-name { font-size: 13px; color: #333; line-height: 1.4; margin-bottom: 5px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } .product-quantity { font-size: 12px; color: #666; } }
       }
-      .col-time { width: 150px; text-align: center; font-size: 13px; color: #666; }
-      .col-type { width: 100px; text-align: center; font-size: 13px; color: #666; }
-      .col-status { width: 100px; text-align: center; .status-text { font-size: 13px; &.success { color: #e60012; } &.warning { color: #e6a23c; } &.danger { color: #f56c6c; } &.info { color: #909399; } } }
-      .col-action { width: 100px; text-align: center; }
+      .col-time {
+        width: 150px;
+        text-align: center;
+        font-size: 13px;
+        color: #666;
+      }
+      .col-type {
+        width: 100px;
+        text-align: center;
+        font-size: 13px;
+        color: #666;
+      }
+      .col-status {
+        width: 100px;
+        text-align: center;
+        .status-text {
+          font-size: 13px;
+          &.success {
+            color: #e60012;
+          }
+          &.warning {
+            color: #e6a23c;
+          }
+          &.danger {
+            color: #f56c6c;
+          }
+          &.info {
+            color: #909399;
+          }
+        }
+      }
+      .col-action {
+        width: 100px;
+        text-align: center;
+      }
     }
   }
 }

+ 223 - 124
src/views/order/orderAudit/index.vue

@@ -28,9 +28,17 @@
     <!-- 筛选栏 -->
     <div class="filter-bar">
       <span class="filter-label">下单部门</span>
-      <el-select v-model="queryParams.department" placeholder="请选择" style="width: 100px" clearable>
-        <el-option label="某某部门" value="某某部门" />
-      </el-select>
+      <el-tree-select
+        v-model="queryParams.department"
+        style="width: 100px"
+        :data="deptList"
+        :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
+        value-key="deptId"
+        placeholder="请选择"
+        check-strictly
+        :render-after-expand="false"
+        clearable
+      ></el-tree-select>
       <span class="filter-label">筛选</span>
       <el-select v-model="queryParams.filter1" placeholder="请选择" style="width: 100px" clearable>
         <el-option label="条件1" value="1" />
@@ -101,9 +109,7 @@
                 <span v-else-if="order.checkStatus === '2'" class="result-text danger">审批驳回</span>
                 <el-button type="primary" link size="small">查看审批流</el-button>
               </template>
-              <el-button v-if="order.fileCount" type="primary" link size="small">
-                审核文件({{ order.fileCount }})
-              </el-button>
+              <el-button v-if="order.fileCount" type="primary" link size="small"> 审核文件({{ order.fileCount }}) </el-button>
             </div>
             <div class="product-cell action-cell">
               <template v-if="activeMainTab === 'myAudit' && order.checkStatus === '0'">
@@ -149,39 +155,45 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted } from 'vue'
-import { useRouter } from 'vue-router'
-import { Search, Document, User, ArrowRight, Picture } from '@element-plus/icons-vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { PageTitle, StatusTabs } from '@/components'
-import { getOrderList, getOrderProducts, checkOrderStatus } from '@/api/pc/enterprise/order'
-
-const router = useRouter()
-
-const activeMainTab = ref('myAudit')
-const activeStatusTab = ref('all')
-const auditDialogVisible = ref(false)
-const auditDialogTitle = ref('审批')
-const auditFormRef = ref()
-const currentAuditOrder = ref<any>(null)
-const currentAuditAction = ref('')
-const loading = ref(false)
-const allOrders = ref<any[]>([])
-const pageNum = ref(1)
-const pageSize = ref(5)
-const total = ref(0)
+import { ref, reactive, computed, onMounted } from 'vue';
+import { useRouter } from 'vue-router';
+import { Search, Document, User, ArrowRight, Picture } from '@element-plus/icons-vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { PageTitle, StatusTabs } from '@/components';
+import { getDeptTree } from '@/api/pc/organization';
+import { DeptInfo } from '@/api/pc/organization/types';
+import { getOrderList, getOrderProducts, checkOrderStatus } from '@/api/pc/enterprise/order';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { complaints_suggestion_type } = toRefs<any>(proxy?.useDict('complaints_suggestion_type'));
+
+const router = useRouter();
+
+const activeMainTab = ref('myAudit');
+const activeStatusTab = ref('all');
+const auditDialogVisible = ref(false);
+const auditDialogTitle = ref('审批');
+const auditFormRef = ref();
+const currentAuditOrder = ref<any>(null);
+const currentAuditAction = ref('');
+const loading = ref(false);
+const allOrders = ref<any[]>([]);
+const pageNum = ref(1);
+const pageSize = ref(5);
+const total = ref(0);
+
+const deptList = ref([]);
 
 const mainTabs = [
   { key: 'myAudit', label: '我审批的', icon: Document },
   { key: 'myApply', label: '我申请的', icon: User }
-]
+];
 
 const auditStatusTabs = [
   { key: 'all', label: '全部订单' },
   { key: 'pending', label: '待审批' },
   { key: 'approved', label: '已通过' },
   { key: 'rejected', label: '已驳回' }
-]
+];
 
 const applyStatusTabs = [
   { key: 'all', label: '全部订单' },
@@ -189,43 +201,43 @@ const applyStatusTabs = [
   { key: 'approved', label: '审批通过' },
   { key: 'rejected', label: '审批驳回' },
   { key: 'cancelled', label: '审批取消' }
-]
+];
 
 const currentStatusTabs = computed(() => {
-  return activeMainTab.value === 'myAudit' ? auditStatusTabs : applyStatusTabs
-})
+  return activeMainTab.value === 'myAudit' ? auditStatusTabs : applyStatusTabs;
+});
 
 const queryParams = reactive({
   keyword: '',
   dateRange: null,
   department: '',
   filter1: ''
-})
+});
 
-const auditForm = reactive({ opinion: '' })
-const auditRules = { opinion: [{ required: true, message: '请输入审批意见', trigger: 'blur' }] }
+const auditForm = reactive({ opinion: '' });
+const auditRules = { opinion: [{ required: true, message: '请输入审批意见', trigger: 'blur' }] };
 
 // 加载订单列表数据
 const loadOrderList = async () => {
   try {
-    loading.value = true
+    loading.value = true;
 
     // 根据状态tab设置checkStatus参数
     const params: any = {
       pageNum: pageNum.value,
       pageSize: pageSize.value
-    }
+    };
 
     // 根据不同状态设置checkStatus参数
     if (activeStatusTab.value === 'pending') {
-      params.checkStatus = '0'
+      params.checkStatus = '0';
     } else if (activeStatusTab.value === 'approved') {
-      params.checkStatus = '1'
+      params.checkStatus = '1';
     } else if (activeStatusTab.value === 'rejected') {
-      params.checkStatus = '2'
+      params.checkStatus = '2';
     }
 
-    const res = await getOrderList(params)
+    const res = await getOrderList(params);
     if (res.code === 200 && res.rows) {
       allOrders.value = res.rows.map((item: any) => ({
         id: item.id,
@@ -237,19 +249,19 @@ const loadOrderList = async () => {
         fileCount: 0,
         checked: false,
         products: []
-      }))
-      total.value = res.total || 0
+      }));
+      total.value = res.total || 0;
 
       // 获取所有订单的商品信息
       if (allOrders.value.length > 0) {
-        const orderIds = allOrders.value.map(order => order.id)
-        const productsRes = await getOrderProducts(orderIds)
+        const orderIds = allOrders.value.map((order) => order.id);
+        const productsRes = await getOrderProducts(orderIds);
         if (productsRes.code === 200 && productsRes.rows) {
           // 将商品信息按订单ID分组
-          const productsByOrderId = new Map()
+          const productsByOrderId = new Map();
           productsRes.rows.forEach((p: any) => {
             if (!productsByOrderId.has(p.orderId)) {
-              productsByOrderId.set(p.orderId, [])
+              productsByOrderId.set(p.orderId, []);
             }
             productsByOrderId.get(p.orderId).push({
               image: p.productImage || '',
@@ -258,122 +270,146 @@ const loadOrderList = async () => {
               spec2: p.productNo || '',
               price: p.orderPrice || 0,
               quantity: p.orderQuantity || 0
-            })
-          })
+            });
+          });
           // 将商品信息赋值给对应的订单
-          allOrders.value.forEach(order => {
-            order.products = productsByOrderId.get(order.id) || []
-          })
+          allOrders.value.forEach((order) => {
+            order.products = productsByOrderId.get(order.id) || [];
+          });
         }
       }
     }
   } catch (error) {
-    console.error('加载订单列表失败:', error)
-    ElMessage.error('加载订单列表失败')
+    console.error('加载订单列表失败:', error);
+    ElMessage.error('加载订单列表失败');
   } finally {
-    loading.value = false
+    loading.value = false;
   }
-}
+};
+
+// 加载部门树
+const loadDeptTree = async () => {
+  try {
+    const res = await getDeptTree();
+    if (res.code === 200 && res.data) {
+      deptList.value = res.data;
+
+      if (Array.isArray(res.data)) {
+        const treeData = proxy?.handleTree<DeptInfo>(res.data, 'deptId', 'parentId');
+        deptList.value = treeData || res.data;
+      } else {
+        deptList.value = [];
+      }
+    }
+  } catch (error) {
+    console.error('获取部门树失败:', error);
+    ElMessage.error('获取部门树失败');
+  }
+};
 
 // 每页条数变化
 const handleSizeChange = () => {
-  pageNum.value = 1
-  loadOrderList()
-}
+  pageNum.value = 1;
+  loadOrderList();
+};
 
 // 页码变化
 const handlePageChange = () => {
-  loadOrderList()
-}
+  loadOrderList();
+};
 
 const orderList = computed(() => {
   // 所有状态都由后端过滤,前端直接返回数据
-  return allOrders.value
-})
+  return allOrders.value;
+});
 
 // 使用后端返回的总数
 const displayTotal = computed(() => {
-  return total.value
-})
+  return total.value;
+});
 
 const handleMainTabChange = () => {
-  activeStatusTab.value = 'all'
-  pageNum.value = 1
-  loadOrderList()
-}
+  activeStatusTab.value = 'all';
+  pageNum.value = 1;
+  loadOrderList();
+};
 
 // 状态 tab 切换
 const handleStatusTabChange = () => {
-  pageNum.value = 1
-  loadOrderList()
-}
+  pageNum.value = 1;
+  loadOrderList();
+};
 
 onMounted(() => {
-  loadOrderList()
-})
+  loadOrderList();
+});
 
 const getStatusText = (checkStatus: string) => {
   const map: Record<string, string> = {
     '0': '待审批',
     '1': '已完成',
     '2': '已完成'
-  }
-  return map[checkStatus] || checkStatus
-}
+  };
+  return map[checkStatus] || checkStatus;
+};
 
 const getStatusClass = (checkStatus: string) => {
-  if (checkStatus === '1' || checkStatus === '2') return 'success'
-  return 'warning'
-}
+  if (checkStatus === '1' || checkStatus === '2') return 'success';
+  return 'warning';
+};
 
 const handleViewDetail = (order: any) => {
-  router.push(`/order/orderManage/detail/${order.id}`)
-}
+  router.push(`/order/orderManage/detail/${order.id}`);
+};
 
 const handleApprove = (order: any) => {
-  currentAuditOrder.value = order
-  currentAuditAction.value = 'approve'
-  auditDialogTitle.value = '审批通过'
-  auditForm.opinion = ''
-  auditDialogVisible.value = true
-}
+  currentAuditOrder.value = order;
+  currentAuditAction.value = 'approve';
+  auditDialogTitle.value = '审批通过';
+  auditForm.opinion = '';
+  auditDialogVisible.value = true;
+};
 
 const handleReject = (order: any) => {
-  currentAuditOrder.value = order
-  currentAuditAction.value = 'reject'
-  auditDialogTitle.value = '审批拒绝'
-  auditForm.opinion = ''
-  auditDialogVisible.value = true
-}
+  currentAuditOrder.value = order;
+  currentAuditAction.value = 'reject';
+  auditDialogTitle.value = '审批拒绝';
+  auditForm.opinion = '';
+  auditDialogVisible.value = true;
+};
 
 const handleCancelApply = (order: any) => {
   ElMessageBox.confirm('确定要取消该申请吗?', '提示', {
-    confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning'
-  }).then(() => {
-    order.auditStatus = 'cancelled'
-    ElMessage.success('已取消申请')
-  }).catch(() => {})
-}
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  })
+    .then(() => {
+      order.auditStatus = 'cancelled';
+      ElMessage.success('已取消申请');
+    })
+    .catch(() => {});
+};
 
 const handleSubmitAudit = async () => {
-  const valid = await auditFormRef.value?.validate()
-  if (!valid) return
+  const valid = await auditFormRef.value?.validate();
+  if (!valid) return;
 
   try {
-    const checkStatus = currentAuditAction.value === 'approve' ? '1' : '2'
+    const checkStatus = currentAuditAction.value === 'approve' ? '1' : '2';
     await checkOrderStatus({
       id: currentAuditOrder.value.id,
       checkStatus: checkStatus
-    })
+    });
 
-    ElMessage.success(currentAuditAction.value === 'approve' ? '审批通过' : '已拒绝')
-    auditDialogVisible.value = false
-    loadOrderList()
+    ElMessage.success(currentAuditAction.value === 'approve' ? '审批通过' : '已拒绝');
+    auditDialogVisible.value = false;
+    loadOrderList();
   } catch (error) {
-    console.error('审批失败:', error)
-    ElMessage.error('审批失败,请重试')
+    console.error('审批失败:', error);
+    ElMessage.error('审批失败,请重试');
   }
-}
+};
 </script>
 
 <style scoped lang="scss">
@@ -396,7 +432,10 @@ const handleSubmitAudit = async () => {
   gap: 10px;
   margin-bottom: 20px;
 
-  .filter-label { font-size: 14px; color: #666; }
+  .filter-label {
+    font-size: 14px;
+    color: #666;
+  }
 }
 
 .order-list {
@@ -416,12 +455,18 @@ const handleSubmitAudit = async () => {
       font-size: 13px;
       color: #666;
 
-      .order-time { color: #333; }
-      .detail-btn { margin-left: auto; }
+      .order-time {
+        color: #333;
+      }
+      .detail-btn {
+        margin-left: auto;
+      }
     }
 
     .product-list {
-      .product-row { display: flex; }
+      .product-row {
+        display: flex;
+      }
 
       .product-cell {
         padding: 15px;
@@ -444,18 +489,43 @@ const handleSubmitAudit = async () => {
           overflow: hidden;
           flex-shrink: 0;
 
-          .el-image { width: 100%; height: 100%; }
-          .image-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
+          .el-image {
+            width: 100%;
+            height: 100%;
+          }
+          .image-placeholder {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+          }
         }
 
         .product-detail {
           flex: 1;
-          .product-name { font-size: 14px; color: #333; margin-bottom: 5px; line-height: 1.4; }
-          .product-spec { font-size: 12px; color: #999; margin-bottom: 5px; }
-          .product-price { font-size: 16px; font-weight: bold; color: #e60012; }
+          .product-name {
+            font-size: 14px;
+            color: #333;
+            margin-bottom: 5px;
+            line-height: 1.4;
+          }
+          .product-spec {
+            font-size: 12px;
+            color: #999;
+            margin-bottom: 5px;
+          }
+          .product-price {
+            font-size: 16px;
+            font-weight: bold;
+            color: #e60012;
+          }
         }
 
-        .product-quantity { font-size: 13px; color: #666; }
+        .product-quantity {
+          font-size: 13px;
+          color: #666;
+        }
       }
 
       .amount-cell {
@@ -464,8 +534,19 @@ const handleSubmitAudit = async () => {
 
         .amount-info {
           margin-bottom: 5px;
-          .label { font-size: 12px; color: #999; }
-          .value { font-size: 14px; color: #333; &.highlight { font-size: 16px; font-weight: bold; color: #e60012; } }
+          .label {
+            font-size: 12px;
+            color: #999;
+          }
+          .value {
+            font-size: 14px;
+            color: #333;
+            &.highlight {
+              font-size: 16px;
+              font-weight: bold;
+              color: #e60012;
+            }
+          }
         }
       }
 
@@ -475,8 +556,26 @@ const handleSubmitAudit = async () => {
         align-items: flex-start;
         gap: 5px;
 
-        .status-text { font-size: 13px; font-weight: 500; &.success { color: #67c23a; } &.warning { color: #e6a23c; } }
-        .result-text { font-size: 13px; font-weight: 500; &.success { color: #e60012; } &.danger { color: #e60012; } }
+        .status-text {
+          font-size: 13px;
+          font-weight: 500;
+          &.success {
+            color: #67c23a;
+          }
+          &.warning {
+            color: #e6a23c;
+          }
+        }
+        .result-text {
+          font-size: 13px;
+          font-weight: 500;
+          &.success {
+            color: #e60012;
+          }
+          &.danger {
+            color: #e60012;
+          }
+        }
       }
 
       .action-cell {

+ 319 - 78
src/views/order/orderEvaluation/index.vue

@@ -5,9 +5,18 @@
 
     <div class="search-bar">
       <el-input v-model="queryParams.keyword" placeholder="搜索" style="width: 150px" clearable>
-        <template #prefix><el-icon><Search /></el-icon></template>
+        <template #prefix
+          ><el-icon><Search /></el-icon
+        ></template>
       </el-input>
-      <el-date-picker v-model="queryParams.dateRange" type="daterange" range-separator="—" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 240px" />
+      <el-date-picker
+        v-model="queryParams.dateRange"
+        type="daterange"
+        range-separator="—"
+        start-placeholder="开始日期"
+        end-placeholder="结束日期"
+        style="width: 240px"
+      />
     </div>
 
     <div class="filter-bar">
@@ -33,14 +42,19 @@
         <div class="order-header">
           <span class="order-time">{{ order.orderTime }}</span>
           <span class="order-no">订单号:{{ order.orderNo }}</span>
-          <el-button type="primary" link class="detail-btn" @click="handleViewDetail(order)">订单详情 <el-icon><ArrowRight /></el-icon></el-button>
+          <el-button type="primary" link class="detail-btn" @click="handleViewDetail(order)"
+            >订单详情 <el-icon><ArrowRight /></el-icon
+          ></el-button>
         </div>
         <div class="product-list">
           <div v-for="(product, productIndex) in order.products" :key="productIndex" class="product-row">
             <div class="product-cell product-info-cell">
               <div class="product-image">
                 <el-image :src="product.image" fit="contain">
-                  <template #error><div class="image-placeholder"><el-icon :size="30" color="#ccc"><Picture /></el-icon></div></template>
+                  <template #error
+                    ><div class="image-placeholder">
+                      <el-icon :size="30" color="#ccc"><Picture /></el-icon></div
+                  ></template>
                 </el-image>
               </div>
               <div class="product-detail">
@@ -51,14 +65,22 @@
               <div class="product-quantity">x{{ product.quantity }}</div>
             </div>
             <div class="product-cell amount-cell" v-if="productIndex === 0">
-              <div class="amount-info"><span class="label">支付款</span><span class="value highlight">¥{{ order.payAmount }}</span></div>
-              <div class="amount-info"><span class="label">{{ order.payMethod }}</span></div>
+              <div class="amount-info">
+                <span class="label">支付款</span><span class="value highlight">¥{{ order.payAmount }}</span>
+              </div>
+              <div class="amount-info">
+                <span class="label">{{ order.payMethod }}</span>
+              </div>
+            </div>
+            <div class="product-cell status-cell" v-if="productIndex === 0">
+              <span class="status-text success">{{ order.statusText }}</span>
             </div>
-            <div class="product-cell status-cell" v-if="productIndex === 0"><span class="status-text success">{{ order.statusText }}</span></div>
             <div class="product-cell action-cell" v-if="productIndex === 0">
               <el-button v-if="activeMainTab === 'pending'" type="primary" link size="small" @click="handleEvaluate(order)">评价</el-button>
               <el-button v-if="activeMainTab === 'followUp'" type="primary" link size="small" @click="handleFollowUpEvaluate(order)">追评</el-button>
-              <el-button v-if="activeMainTab === 'evaluated'" type="primary" link size="small" @click="handleViewEvaluation(order)">查看评价</el-button>
+              <el-button v-if="activeMainTab === 'evaluated'" type="primary" link size="small" @click="handleViewEvaluation(order)"
+                >查看评价</el-button
+              >
             </div>
           </div>
         </div>
@@ -70,7 +92,10 @@
       <div class="evaluate-product">
         <div class="product-image">
           <el-image :src="currentProduct?.image" fit="contain">
-            <template #error><div class="image-placeholder"><el-icon :size="30" color="#ccc"><Picture /></el-icon></div></template>
+            <template #error
+              ><div class="image-placeholder">
+                <el-icon :size="30" color="#ccc"><Picture /></el-icon></div
+            ></template>
           </el-image>
         </div>
         <div class="product-info">
@@ -79,8 +104,8 @@
         </div>
       </div>
       <el-form ref="evaluateFormRef" :model="evaluateForm" :rules="evaluateRules" label-width="80px">
-        <el-form-item label="商品评分" prop="rating">
-          <el-rate v-model="evaluateForm.rating" :colors="['#e60012', '#e60012', '#e60012']" />
+        <el-form-item label="商品评分" prop="deliverGoods">
+          <el-rate v-model="evaluateForm.deliverGoods" :colors="['#e60012', '#e60012', '#e60012']" />
         </el-form-item>
         <el-form-item label="评价内容" prop="content">
           <el-input v-model="evaluateForm.content" type="textarea" :rows="4" placeholder="请输入评价内容" maxlength="200" show-word-limit />
@@ -100,108 +125,324 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed } from 'vue'
-import { useRouter } from 'vue-router'
-import { Search, Edit, ChatDotRound, Document, ArrowRight, Picture, Plus } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
-import { PageTitle, StatusTabs } from '@/components'
+import { ref, reactive, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { Search, Edit, ChatDotRound, Document, ArrowRight, Picture, Plus } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+import { PageTitle, StatusTabs } from '@/components';
 
-const router = useRouter()
-const activeMainTab = ref('pending')
-const evaluateDialogVisible = ref(false)
-const evaluateDialogTitle = ref('商品评价')
-const evaluateFormRef = ref()
-const currentOrder = ref<any>(null)
-const currentProduct = ref<any>(null)
+const router = useRouter();
+const activeMainTab = ref('pending');
+const evaluateDialogVisible = ref(false);
+const evaluateDialogTitle = ref('商品评价');
+const evaluateFormRef = ref();
+const currentOrder = ref<any>(null);
+const currentProduct = ref<any>(null);
 
 const mainTabs = [
   { key: 'pending', label: '待评价', icon: Edit },
   { key: 'followUp', label: '待追评', icon: ChatDotRound },
   { key: 'evaluated', label: '已评价', icon: Document }
-]
+];
 
-const queryParams = reactive({ keyword: '', dateRange: null, department: '', status: '', payType: '' })
-const evaluateForm = reactive({ rating: 5, content: '' })
+const queryParams = reactive({ keyword: '', dateRange: null, department: '', status: '', payType: '' });
+const evaluateForm = reactive({ deliverGoods: 5, content: '' });
 const evaluateRules = {
-  rating: [{ required: true, message: '请选择评分', trigger: 'change' }],
+  deliverGoods: [{ required: true, message: '请选择评分', trigger: 'change' }],
   content: [{ required: true, message: '请输入评价内容', trigger: 'blur' }]
-}
+};
 
 const pendingOrders = ref([
-  { orderTime: '2025/12/05 16:15:06', orderNo: '489283929283298392', payAmount: '181', payMethod: '微信支付', statusText: '已完成', products: [{ id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }] },
-  { orderTime: '2025/12/05 16:15:06', orderNo: '489283929283298393', payAmount: '181', payMethod: '微信支付', statusText: '已完成', products: [{ id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }] },
-  { orderTime: '2025/12/05 16:15:06', orderNo: '489283929283298394', payAmount: '181', payMethod: '微信支付', statusText: '已完成', products: [{ id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }] }
-])
+  {
+    orderTime: '2025/12/05 16:15:06',
+    orderNo: '489283929283298392',
+    payAmount: '181',
+    payMethod: '微信支付',
+    statusText: '已完成',
+    products: [
+      { id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }
+    ]
+  },
+  {
+    orderTime: '2025/12/05 16:15:06',
+    orderNo: '489283929283298393',
+    payAmount: '181',
+    payMethod: '微信支付',
+    statusText: '已完成',
+    products: [
+      { id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }
+    ]
+  },
+  {
+    orderTime: '2025/12/05 16:15:06',
+    orderNo: '489283929283298394',
+    payAmount: '181',
+    payMethod: '微信支付',
+    statusText: '已完成',
+    products: [
+      { id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }
+    ]
+  }
+]);
 
 const followUpOrders = ref([
-  { orderTime: '2025/12/05 16:15:06', orderNo: '489283929283298395', payAmount: '181', payMethod: '微信支付', statusText: '已完成', products: [{ id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }] }
-])
+  {
+    orderTime: '2025/12/05 16:15:06',
+    orderNo: '489283929283298395',
+    payAmount: '181',
+    payMethod: '微信支付',
+    statusText: '已完成',
+    products: [
+      { id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }
+    ]
+  }
+]);
 
 const evaluatedOrders = ref([
-  { orderTime: '2025/12/05 16:15:06', orderNo: '489283929283298396', payAmount: '181', payMethod: '微信支付', statusText: '已完成', products: [{ id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }] }
-])
+  {
+    orderTime: '2025/12/05 16:15:06',
+    orderNo: '489283929283298396',
+    payAmount: '181',
+    payMethod: '微信支付',
+    statusText: '已完成',
+    products: [
+      { id: 1, name: '清华同方超越E500台式机电脑(i3-6100/4G/1T/19.5寸)', spec1: '规格02', spec2: '规格01', price: '181', quantity: 1, image: '' }
+    ]
+  }
+]);
 
 const orderList = computed(() => {
-  if (activeMainTab.value === 'pending') return pendingOrders.value
-  else if (activeMainTab.value === 'followUp') return followUpOrders.value
-  else return evaluatedOrders.value
-})
-
+  if (activeMainTab.value === 'pending') return pendingOrders.value;
+  else if (activeMainTab.value === 'followUp') return followUpOrders.value;
+  else return evaluatedOrders.value;
+});
 
-const handleViewDetail = (order: any) => { router.push(`/order/orderManage/detail/${order.orderNo}`) }
-const handleEvaluate = (order: any) => { currentOrder.value = order; currentProduct.value = order.products[0]; evaluateDialogTitle.value = '商品评价'; evaluateForm.rating = 5; evaluateForm.content = ''; evaluateDialogVisible.value = true }
-const handleFollowUpEvaluate = (order: any) => { currentOrder.value = order; currentProduct.value = order.products[0]; evaluateDialogTitle.value = '追加评价'; evaluateForm.rating = 5; evaluateForm.content = ''; evaluateDialogVisible.value = true }
-const handleViewEvaluation = (order: any) => { ElMessage.info('查看评价详情') }
+const handleViewDetail = (order: any) => {
+  router.push(`/order/orderManage/detail/${order.orderId}`);
+};
+const handleEvaluate = (order: any) => {
+  currentOrder.value = order;
+  currentProduct.value = order.products[0];
+  evaluateDialogTitle.value = '商品评价';
+  evaluateForm.deliverGoods = undefined;
+  evaluateForm.content = '';
+  evaluateDialogVisible.value = true;
+};
+const handleFollowUpEvaluate = (order: any) => {
+  currentOrder.value = order;
+  currentProduct.value = order.products[0];
+  evaluateDialogTitle.value = '追加评价';
+  evaluateForm.deliverGoods = 5;
+  evaluateForm.content = '';
+  evaluateDialogVisible.value = true;
+};
+const handleViewEvaluation = (order: any) => {
+  ElMessage.info('查看评价详情');
+};
 const handleSubmitEvaluate = async () => {
-  const valid = await evaluateFormRef.value?.validate()
-  if (!valid) return
-  ElMessage.success('评价提交成功')
-  evaluateDialogVisible.value = false
+  const valid = await evaluateFormRef.value?.validate();
+  if (!valid) return;
+  ElMessage.success('评价提交成功');
+  evaluateDialogVisible.value = false;
   if (activeMainTab.value === 'pending') {
-    const index = pendingOrders.value.findIndex(o => o.orderNo === currentOrder.value.orderNo)
-    if (index > -1) { pendingOrders.value.splice(index, 1); followUpOrders.value.push(currentOrder.value) }
+    const index = pendingOrders.value.findIndex((o) => o.orderNo === currentOrder.value.orderNo);
+    if (index > -1) {
+      pendingOrders.value.splice(index, 1);
+      followUpOrders.value.push(currentOrder.value);
+    }
   } else if (activeMainTab.value === 'followUp') {
-    const index = followUpOrders.value.findIndex(o => o.orderNo === currentOrder.value.orderNo)
-    if (index > -1) { followUpOrders.value.splice(index, 1); evaluatedOrders.value.push(currentOrder.value) }
+    const index = followUpOrders.value.findIndex((o) => o.orderNo === currentOrder.value.orderNo);
+    if (index > -1) {
+      followUpOrders.value.splice(index, 1);
+      evaluatedOrders.value.push(currentOrder.value);
+    }
   }
-}
+};
 </script>
 
 <style scoped lang="scss">
-.order-evaluation-container { padding: 20px; background: #fff; min-height: 100%; }
-.search-bar { display: flex; align-items: center; gap: 15px; margin-bottom: 15px; }
-.filter-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 20px; .filter-label { font-size: 14px; color: #666; } }
+.order-evaluation-container {
+  padding: 20px;
+  background: #fff;
+  min-height: 100%;
+}
+.search-bar {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  margin-bottom: 15px;
+}
+.filter-bar {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-bottom: 20px;
+  .filter-label {
+    font-size: 14px;
+    color: #666;
+  }
+}
 .order-list {
-  .order-card { border: 1px solid #eee; border-radius: 4px; margin-bottom: 15px; overflow: hidden;
-    .order-header { display: flex; align-items: center; gap: 15px; padding: 12px 15px; background: #f9f9f9; border-bottom: 1px solid #eee; font-size: 13px; color: #666;
-      .order-time { color: #333; } .detail-btn { margin-left: auto; }
+  .order-card {
+    border: 1px solid #eee;
+    border-radius: 4px;
+    margin-bottom: 15px;
+    overflow: hidden;
+    .order-header {
+      display: flex;
+      align-items: center;
+      gap: 15px;
+      padding: 12px 15px;
+      background: #f9f9f9;
+      border-bottom: 1px solid #eee;
+      font-size: 13px;
+      color: #666;
+      .order-time {
+        color: #333;
+      }
+      .detail-btn {
+        margin-left: auto;
+      }
     }
     .product-list {
-      .product-row { display: flex; border-bottom: 1px solid #f5f5f5; &:last-child { border-bottom: none; } }
-      .product-cell { padding: 15px; display: flex; flex-direction: column; justify-content: center; }
-      .product-info-cell { flex: 1; flex-direction: row; align-items: center; gap: 15px;
-        .product-image { width: 80px; height: 80px; background: #f5f5f5; border-radius: 4px; overflow: hidden; flex-shrink: 0;
-          .el-image { width: 100%; height: 100%; } .image-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
+      .product-row {
+        display: flex;
+        border-bottom: 1px solid #f5f5f5;
+        &:last-child {
+          border-bottom: none;
+        }
+      }
+      .product-cell {
+        padding: 15px;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+      }
+      .product-info-cell {
+        flex: 1;
+        flex-direction: row;
+        align-items: center;
+        gap: 15px;
+        .product-image {
+          width: 80px;
+          height: 80px;
+          background: #f5f5f5;
+          border-radius: 4px;
+          overflow: hidden;
+          flex-shrink: 0;
+          .el-image {
+            width: 100%;
+            height: 100%;
+          }
+          .image-placeholder {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+          }
+        }
+        .product-detail {
+          flex: 1;
+          .product-name {
+            font-size: 14px;
+            color: #333;
+            margin-bottom: 5px;
+            line-height: 1.4;
+          }
+          .product-spec {
+            font-size: 12px;
+            color: #999;
+            margin-bottom: 5px;
+          }
+          .product-price {
+            font-size: 16px;
+            font-weight: bold;
+            color: #e60012;
+          }
         }
-        .product-detail { flex: 1;
-          .product-name { font-size: 14px; color: #333; margin-bottom: 5px; line-height: 1.4; }
-          .product-spec { font-size: 12px; color: #999; margin-bottom: 5px; }
-          .product-price { font-size: 16px; font-weight: bold; color: #e60012; }
+        .product-quantity {
+          font-size: 13px;
+          color: #666;
         }
-        .product-quantity { font-size: 13px; color: #666; }
       }
-      .amount-cell { width: 120px; border-left: 1px solid #f5f5f5;
-        .amount-info { margin-bottom: 5px; .label { font-size: 12px; color: #999; } .value { font-size: 14px; color: #333; &.highlight { font-size: 16px; font-weight: bold; color: #e60012; } } }
+      .amount-cell {
+        width: 120px;
+        border-left: 1px solid #f5f5f5;
+        .amount-info {
+          margin-bottom: 5px;
+          .label {
+            font-size: 12px;
+            color: #999;
+          }
+          .value {
+            font-size: 14px;
+            color: #333;
+            &.highlight {
+              font-size: 16px;
+              font-weight: bold;
+              color: #e60012;
+            }
+          }
+        }
+      }
+      .status-cell {
+        width: 80px;
+        border-left: 1px solid #f5f5f5;
+        align-items: center;
+        .status-text {
+          font-size: 13px;
+          font-weight: 500;
+          &.success {
+            color: #e60012;
+          }
+        }
+      }
+      .action-cell {
+        width: 80px;
+        border-left: 1px solid #f5f5f5;
+        align-items: center;
       }
-      .status-cell { width: 80px; border-left: 1px solid #f5f5f5; align-items: center; .status-text { font-size: 13px; font-weight: 500; &.success { color: #e60012; } } }
-      .action-cell { width: 80px; border-left: 1px solid #f5f5f5; align-items: center; }
     }
   }
 }
-.evaluate-product { display: flex; align-items: center; gap: 15px; padding: 15px; background: #f9f9f9; border-radius: 4px; margin-bottom: 20px;
-  .product-image { width: 60px; height: 60px; background: #fff; border-radius: 4px; overflow: hidden; flex-shrink: 0;
-    .el-image { width: 100%; height: 100%; } .image-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
+.evaluate-product {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  padding: 15px;
+  background: #f9f9f9;
+  border-radius: 4px;
+  margin-bottom: 20px;
+  .product-image {
+    width: 60px;
+    height: 60px;
+    background: #fff;
+    border-radius: 4px;
+    overflow: hidden;
+    flex-shrink: 0;
+    .el-image {
+      width: 100%;
+      height: 100%;
+    }
+    .image-placeholder {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+  }
+  .product-info {
+    .product-name {
+      font-size: 14px;
+      color: #333;
+      margin-bottom: 5px;
+    }
+    .product-spec {
+      font-size: 12px;
+      color: #999;
+    }
   }
-  .product-info { .product-name { font-size: 14px; color: #333; margin-bottom: 5px; } .product-spec { font-size: 12px; color: #999; } }
 }
 </style>

+ 492 - 156
src/views/order/orderManage/index.vue

@@ -4,23 +4,61 @@
     <!-- 搜索栏 -->
     <div class="search-bar">
       <el-input v-model="queryParams.keyword" placeholder="搜索订单号" style="width: 200px" clearable @keyup.enter="handleQuery">
-        <template #prefix><el-icon><Search /></el-icon></template>
+        <template #prefix
+          ><el-icon><Search /></el-icon
+        ></template>
       </el-input>
-      <el-date-picker v-model="queryParams.dateRange" type="daterange" range-separator="—" start-placeholder="开始日期" end-placeholder="结束日期" style="width: 240px" />
+      <el-date-picker
+        v-model="queryParams.dateRange"
+        type="daterange"
+        range-separator="—"
+        start-placeholder="开始日期"
+        end-placeholder="结束日期"
+        style="width: 240px"
+      />
       <el-button type="primary" @click="handleQuery">搜索</el-button>
       <el-button @click="handleReset">重置</el-button>
     </div>
     <!-- 筛选栏 -->
     <div class="filter-bar">
       <span class="filter-label">下单部门</span>
-      <el-select v-model="queryParams.department" placeholder="请选择" style="width: 100px" clearable><el-option label="某某部门" value="某某部门" /></el-select>
+      <el-tree-select
+        v-model="queryParams.department"
+        style="width: 100px"
+        :data="deptList"
+        :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
+        value-key="deptId"
+        placeholder="请选择"
+        check-strictly
+        :render-after-expand="false"
+        clearable
+      >
+      </el-tree-select>
       <span class="filter-label">状态</span>
-      <el-select v-model="queryParams.status" placeholder="请选择" style="width: 100px" clearable><el-option label="已完成" value="completed" /><el-option label="待收货" value="pending" /><el-option label="已取消" value="cancelled" /></el-select>
+      <el-select v-model="queryParams.status" placeholder="请选择" style="width: 100px" clearable>
+        <el-option v-for="dict in order_status" :key="dict.value" :label="dict.label" :value="dict.value" />
+      </el-select>
       <span class="filter-label">支付方式</span>
-      <el-select v-model="queryParams.payType" placeholder="请选择" style="width: 100px" clearable><el-option label="在线支付" value="online" /><el-option label="账期支付" value="credit" /></el-select>
+      <el-select v-model="queryParams.payType" placeholder="请选择" style="width: 100px" clearable
+        ><el-option v-for="dict in pay_method" :key="dict.value" :label="dict.label" :value="dict.value"
+      /></el-select>
       <div class="filter-right">
-        <el-dropdown><el-button>订单导出 <el-icon><ArrowDown /></el-icon></el-button><template #dropdown><el-dropdown-menu><el-dropdown-item>导出Excel</el-dropdown-item><el-dropdown-item>导出PDF</el-dropdown-item></el-dropdown-menu></template></el-dropdown>
-        <el-dropdown><el-button>订单打印 <el-icon><ArrowDown /></el-icon></el-button><template #dropdown><el-dropdown-menu><el-dropdown-item>打印订单</el-dropdown-item><el-dropdown-item>打印发货单</el-dropdown-item></el-dropdown-menu></template></el-dropdown>
+        <el-dropdown
+          ><el-button
+            >订单导出 <el-icon><ArrowDown /></el-icon></el-button
+          ><template #dropdown
+            ><el-dropdown-menu><el-dropdown-item>导出Excel</el-dropdown-item><el-dropdown-item>导出PDF</el-dropdown-item></el-dropdown-menu></template
+          ></el-dropdown
+        >
+        <el-dropdown
+          ><el-button
+            >订单打印 <el-icon><ArrowDown /></el-icon></el-button
+          ><template #dropdown
+            ><el-dropdown-menu
+              ><el-dropdown-item>打印订单</el-dropdown-item><el-dropdown-item>打印发货单</el-dropdown-item></el-dropdown-menu
+            ></template
+          ></el-dropdown
+        >
         <el-button>下载电子签单</el-button>
       </div>
     </div>
@@ -31,24 +69,34 @@
           <el-icon><component :is="tab.icon" /></el-icon><span>{{ tab.label }}</span>
         </div>
       </div>
-      <el-button type="danger" link><el-icon><Delete /></el-icon>订单回收站</el-button>
+      <el-button type="danger" link
+        ><el-icon><Delete /></el-icon>订单回收站</el-button
+      >
     </div>
     <!-- 订单列表 -->
     <div class="order-list">
-      <div v-for="(order, orderIndex) in orderList" :key="order.id" class="order-card">
+      <div v-for="order in orderList" :key="order.id" class="order-card">
         <div class="order-header">
           <el-checkbox v-model="order.checked" @change="handleOrderCheck" />
           <span class="order-time">{{ order.orderTime }}</span>
           <span class="order-info">订单号:{{ order.orderNo }}</span>
           <span class="order-info">下单人:{{ order.orderPerson }}</span>
           <span class="order-info">部门:{{ order.department }}</span>
-          <el-button type="primary" link class="expand-btn" @click="handleExpand(order)">{{ order.expanded ? '收起' : '展开' }} <el-icon><ArrowDown /></el-icon></el-button>
+          <el-button type="primary" link class="expand-btn" @click="handleExpand(order)"
+            >{{ order.expanded ? '收起' : '展开' }} <el-icon><ArrowDown /></el-icon
+          ></el-button>
         </div>
         <div v-if="order.countdown" class="countdown-bar">订单锁定剩余时间:{{ order.countdown }}</div>
         <div class="product-list">
-          <div v-for="(item, itemIndex) in (order.expanded ? order.products : order.products.slice(0, 1))" :key="itemIndex" class="product-row">
+          <div v-for="(item, itemIndex) in order.expanded ? order.products : order.products.slice(0, 1)" :key="itemIndex" class="product-row">
             <div class="product-cell product-info-cell">
-              <div class="product-image"><el-image :src="item.image" fit="contain"><template #error><div class="image-placeholder"><el-icon :size="30" color="#ccc"><Picture /></el-icon></div></template></el-image></div>
+              <div class="product-image">
+                <el-image :src="item.image" fit="contain"
+                  ><template #error
+                    ><div class="image-placeholder">
+                      <el-icon :size="30" color="#ccc"><Picture /></el-icon></div></template
+                ></el-image>
+              </div>
               <div class="product-detail">
                 <div class="product-name">{{ item.name }}</div>
                 <div class="product-spec">{{ item.spec1 }} {{ item.spec2 }}</div>
@@ -57,17 +105,32 @@
               <div class="product-quantity">x{{ item.quantity }}</div>
             </div>
             <div class="product-cell amount-cell" v-if="itemIndex === 0">
-              <div class="amount-info"><span class="label">支付款</span><span class="value highlight">¥{{ order.payAmount }}</span></div>
-              <div class="amount-info"><span class="label">含运费:</span><span class="value">¥{{ order.freight }}</span></div>
+              <div class="amount-info">
+                <span class="label">支付款</span><span class="value highlight">¥{{ order.payAmount }}</span>
+              </div>
+              <div class="amount-info">
+                <span class="label">含运费:</span><span class="value">¥{{ order.freight }}</span>
+              </div>
             </div>
             <div class="product-cell status-cell" v-if="itemIndex === 0">
               <span class="status-text" :style="{ color: getStatusColor(order.status) }">{{ order.statusText }}</span>
               <el-button type="primary" link size="small" @click="handleViewDetail(order)">查看订单轨迹</el-button>
-              <template v-if="order.auditStatus"><span :class="['audit-status', getAuditStatusClass(order.auditStatus)]">{{ order.auditStatus }}</span><el-button type="primary" link size="small">查看审批流</el-button></template>
+              <template v-if="order.auditStatus"
+                ><span :class="['audit-status', getAuditStatusClass(order.auditStatus)]">{{ order.auditStatus }}</span
+                ><el-button type="primary" link size="small">查看审批流</el-button></template
+              >
               <el-button v-if="order.fileCount" type="primary" link size="small">审核文件({{ order.fileCount }})</el-button>
             </div>
             <div class="product-cell action-cell" v-if="itemIndex === 0">
-              <el-button v-for="action in getOrderActions(order)" :key="action" type="primary" link size="small" @click="handleAction(action, order)">{{ action }}</el-button>
+              <el-button
+                v-for="action in getOrderActions(order)"
+                :key="action"
+                type="primary"
+                link
+                size="small"
+                @click="handleAction(action, order)"
+                >{{ action }}</el-button
+              >
             </div>
           </div>
         </div>
@@ -78,9 +141,20 @@
     <div class="bottom-bar">
       <div class="bottom-left"><el-checkbox v-model="selectAll" @change="handleSelectAll">全选</el-checkbox></div>
       <div class="bottom-right">
-        <span class="selected-info">已勾选 <em>{{ selectedCount }}</em>/{{ totalOrders }}个订单 共计<em>¥{{ selectedAmount }}</em></span>
+        <span class="selected-info"
+          >已勾选 <em>{{ selectedCount }}</em
+          >/{{ totalOrders }}个订单 共计<em>¥{{ selectedAmount }}</em></span
+        >
         <el-button>复制订单号</el-button>
-        <el-dropdown><el-button>批量订单打印 <el-icon><ArrowDown /></el-icon></el-button><template #dropdown><el-dropdown-menu><el-dropdown-item>打印订单</el-dropdown-item><el-dropdown-item>打印发货单</el-dropdown-item></el-dropdown-menu></template></el-dropdown>
+        <el-dropdown
+          ><el-button
+            >批量订单打印 <el-icon><ArrowDown /></el-icon></el-button
+          ><template #dropdown
+            ><el-dropdown-menu
+              ><el-dropdown-item>打印订单</el-dropdown-item><el-dropdown-item>打印发货单</el-dropdown-item></el-dropdown-menu
+            ></template
+          ></el-dropdown
+        >
         <el-button type="danger">批量确认收货</el-button>
       </div>
     </div>
@@ -90,19 +164,25 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted, watch } from 'vue'
-import { useRouter } from 'vue-router'
-import { Search, ArrowDown, Delete, Document, Clock, Box, CircleCheck, CircleClose, Picture } from '@element-plus/icons-vue'
-import type { CheckboxValueType } from 'element-plus'
-import { TablePagination } from '@/components'
-import { getOrderList, getOrderStatusStats, getOrderProducts, cancelOrder, deleteOrder } from '@/api/pc/enterprise/order'
-import type { OrderMain, OrderStatusStats } from '@/api/pc/enterprise/orderTypes'
-import { ElMessage, ElMessageBox } from 'element-plus'
+import { ref, reactive, computed, onMounted, watch } from 'vue';
+import { useRouter } from 'vue-router';
+import { Search, ArrowDown, Delete, Document, Clock, Box, CircleCheck, CircleClose, Picture } from '@element-plus/icons-vue';
+import type { CheckboxValueType } from 'element-plus';
+import { TablePagination } from '@/components';
+import { getOrderList, getOrderStatusStats, getOrderProducts, cancelOrder, deleteOrder } from '@/api/pc/enterprise/order';
+import type { OrderMain, OrderStatusStats } from '@/api/pc/enterprise/orderTypes';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { getDeptTree } from '@/api/pc/organization';
+import { DeptInfo } from '@/api/pc/organization/types';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { order_status, pay_method } = toRefs<any>(proxy?.useDict('order_status', 'pay_method'));
+
+const router = useRouter();
+const activeTab = ref('all');
+const selectAll = ref(false);
+const loading = ref(false);
 
-const router = useRouter()
-const activeTab = ref('all')
-const selectAll = ref(false)
-const loading = ref(false)
+const deptList = ref([]);
 
 const statusTabs = [
   { key: 'all', label: '全部订单', icon: Document },
@@ -110,45 +190,45 @@ const statusTabs = [
   { key: 'shipping', label: '待收货', icon: Box },
   { key: 'completed', label: '已完成', icon: CircleCheck },
   { key: 'cancelled', label: '已取消', icon: CircleClose }
-]
+];
 
 // 监听标签页切换,重置页码并重新获取数据
 watch(activeTab, (newTab) => {
-  queryParams.pageNum = 1
+  queryParams.pageNum = 1;
   // 根据标签页设置后端查询的状态参数
   if (newTab === 'all') {
-    queryParams.status = ''
+    queryParams.status = '';
   } else {
     // 将前端标签页key映射回后端状态值
     const tabToStatusMap: Record<string, string> = {
-      'preOrder': '0,1',      // 待支付,待确认
-      'shipping': '2,3,4',    // 待发货,部分发货,发货完成
-      'completed': '5',       // 已完成
-      'cancelled': '6,7'      // 已关闭,已取消
-    }
-    queryParams.status = tabToStatusMap[newTab] || ''
+      'preOrder': '1', // 待支付,待确认
+      'shipping': '2,3,4', // 待发货,部分发货,发货完成
+      'completed': '5', // 已完成
+      'cancelled': '7' // 已关闭,已取消
+    };
+    queryParams.status = tabToStatusMap[newTab] || '';
   }
-  fetchOrderList()
-})
+  fetchOrderList();
+});
 
-const queryParams = reactive({ keyword: '', dateRange: null, department: '', status: '', payType: '', pageNum: 1, pageSize: 5 })
-const total = ref(0)
-const allOrders = ref<any[]>([])
+const queryParams = reactive({ keyword: '', dateRange: null, department: '', status: '', payType: '', pageNum: 1, pageSize: 5 });
+const total = ref(0);
+const allOrders = ref<any[]>([]);
 
 // 将后端状态映射为前端标签页key
 const mapBackendStatusToTabKey = (backendStatus: string) => {
   const statusMap: Record<string, string> = {
-    '0': 'preOrder',      // 待支付
-    '1': 'preOrder',      // 待确认
-    '2': 'shipping',      // 待发货
-    '3': 'shipping',      // 部分发货
-    '4': 'shipping',      // 发货完成
-    '5': 'completed',     // 已完成
-    '6': 'cancelled',     // 已关闭
-    '7': 'cancelled'      // 已取消
-  }
-  return statusMap[backendStatus] || backendStatus
-}
+    '0': 'preOrder', // 待支付
+    '1': 'preOrder', // 待确认
+    '2': 'shipping', // 待发货
+    '3': 'shipping', // 部分发货
+    '4': 'shipping', // 发货完成
+    '5': 'completed', // 已完成
+    '6': 'cancelled', // 已关闭
+    '7': 'cancelled' // 已取消
+  };
+  return statusMap[backendStatus] || backendStatus;
+};
 
 // 根据订单状态获取状态文本
 const getStatusText = (status: string) => {
@@ -161,37 +241,57 @@ const getStatusText = (status: string) => {
     '5': '已完成',
     '6': '已关闭',
     '7': '已取消'
+  };
+  return statusMap[status] || status;
+};
+
+// 加载部门树
+const loadDeptTree = async () => {
+  try {
+    const res = await getDeptTree();
+    if (res.code === 200 && res.data) {
+      deptList.value = res.data;
+
+      if (Array.isArray(res.data)) {
+        const treeData = proxy?.handleTree<DeptInfo>(res.data, 'deptId', 'parentId');
+        deptList.value = treeData || res.data;
+      } else {
+        deptList.value = [];
+      }
+    }
+  } catch (error) {
+    console.error('获取部门树失败:', error);
+    ElMessage.error('获取部门树失败');
   }
-  return statusMap[status] || status
-}
+};
 
 // 获取订单列表
 const fetchOrderList = async () => {
-  loading.value = true
+  loading.value = true;
   try {
     const params: any = {
       pageNum: queryParams.pageNum,
       pageSize: queryParams.pageSize
-    }
+    };
 
     // 添加筛选条件
-    if (queryParams.keyword) params.orderNo = queryParams.keyword
-    if (queryParams.department) params.department = queryParams.department
-    if (queryParams.status) params.orderStatuses = queryParams.status  // 使用orderStatuses支持多状态查询
-    if (queryParams.payType) params.payType = queryParams.payType
+    if (queryParams.keyword) params.orderNo = queryParams.keyword;
+    if (queryParams.department) params.department = queryParams.department;
+    if (queryParams.status) params.orderStatuses = queryParams.status; // 使用orderStatuses支持多状态查询
+    if (queryParams.payType) params.payType = queryParams.payType;
     if (queryParams.dateRange && queryParams.dateRange.length === 2) {
-      params.beginTime = queryParams.dateRange[0]
-      params.endTime = queryParams.dateRange[1]
+      params.beginTime = queryParams.dateRange[0];
+      params.endTime = queryParams.dateRange[1];
     }
 
-    console.log('发送到后端的参数:', params)
-    const res = await getOrderList(params)
-    console.log('后端返回的数据:', res)
+    console.log('发送到后端的参数:', params);
+    const res = await getOrderList(params);
+    console.log('后端返回的数据:', res);
     if (res.code === 200) {
       // 调试:打印后端返回的第一条订单数据
       if (res.rows && res.rows.length > 0) {
-        console.log('后端���回的订单状态值:', res.rows[0].orderStatus)
-        console.log('完整的订单数据:', res.rows[0])
+        console.log('后端���回的订单状态值:', res.rows[0].orderStatus);
+        console.log('完整的订单数据:', res.rows[0]);
       }
 
       // 将后端数据转换为前端需要的格式
@@ -211,52 +311,69 @@ const fetchOrderList = async () => {
         checked: false,
         expanded: false,
         products: [] // 商品信息需要单独加载
-      }))
+      }));
 
       // 调试:打印转换后的订单状态
-      console.log('转换后的订单状态分布:', allOrders.value.map(o => o.status))
+      console.log(
+        '转换后的订单状态分布:',
+        allOrders.value.map((o) => o.status)
+      );
 
-      total.value = res.total || 0
+      total.value = res.total || 0;
     }
   } catch (error) {
-    console.error('获取订单列表失败:', error)
+    console.error('获取订单列表失败:', error);
   } finally {
-    loading.value = false
+    loading.value = false;
   }
-}
+};
 
-const orderList = computed(() => allOrders.value)
-const totalOrders = computed(() => total.value)
-const selectedCount = computed(() => orderList.value.filter(o => o.checked).length)
-const selectedAmount = computed(() => orderList.value.filter(o => o.checked).reduce((sum, o) => sum + parseFloat(o.payAmount), 0).toFixed(2))
+const orderList = computed(() => allOrders.value);
+const totalOrders = computed(() => total.value);
+const selectedCount = computed(() => orderList.value.filter((o) => o.checked).length);
+const selectedAmount = computed(() =>
+  orderList.value
+    .filter((o) => o.checked)
+    .reduce((sum, o) => sum + parseFloat(o.payAmount), 0)
+    .toFixed(2)
+);
 
-const getStatusColor = (status: string) => ({ completed: '#67c23a', preOrder: '#e6a23c', shipping: '#409eff', cancelled: '#909399' }[status] || '#909399')
-const getAuditStatusClass = (auditStatus: string) => auditStatus === '审批通过' ? 'success' : auditStatus === '审批驳回' ? 'danger' : 'warning'
+const getStatusColor = (status: string) =>
+  ({ completed: '#67c23a', preOrder: '#e6a23c', shipping: '#409eff', cancelled: '#909399' })[status] || '#909399';
+const getAuditStatusClass = (auditStatus: string) => (auditStatus === '审批通过' ? 'success' : auditStatus === '审批驳回' ? 'danger' : 'warning');
 const getOrderActions = (order: any) => {
-  const actions: string[] = []
+  const actions: string[] = [];
   switch (order.status) {
-    case 'preOrder': actions.push('再次购买', '加入采购单', '取消订单'); break
-    case 'shipping': actions.push('再次购买', '加入采购单', '取消订单'); break
-    case 'completed': actions.push('评价', '再次购买', '加入采购单', '申请售后', '删除订单', '查看发票'); break
-    case 'cancelled': actions.push('再次购买', '加入采购单', '删除订单'); break
+    case 'preOrder':
+      actions.push('再次购买', '加入采购单', '取消订单');
+      break;
+    case 'shipping':
+      actions.push('再次购买', '加入采购单', '取消订单');
+      break;
+    case 'completed':
+      actions.push('评价', '再次购买', '加入采购单', '申请售后', '删除订单', '查看发票');
+      break;
+    case 'cancelled':
+      actions.push('再次购买', '加入采购单', '删除订单');
+      break;
   }
-  return actions
-}
+  return actions;
+};
 const handleExpand = async (order: any) => {
-  console.log('handleExpand 被调用, order.id:', order.id, 'order.expanded:', order.expanded)
+  console.log('handleExpand 被调用, order.id:', order.id, 'order.expanded:', order.expanded);
 
-  const orderIndex = allOrders.value.findIndex(o => o.id === order.id)
-  if (orderIndex === -1) return
+  const orderIndex = allOrders.value.findIndex((o) => o.id === order.id);
+  if (orderIndex === -1) return;
 
-  const currentOrder = allOrders.value[orderIndex]
-  console.log('找到订单, currentOrder.expanded:', currentOrder.expanded)
+  const currentOrder = allOrders.value[orderIndex];
+  console.log('找到订单, currentOrder.expanded:', currentOrder.expanded);
 
   if (!currentOrder.expanded && currentOrder.products.length === 0) {
-    console.log('需要加载商品')
+    console.log('需要加载商品');
     try {
-      const res = await getOrderProducts([order.id])
+      const res = await getOrderProducts([order.id]);
       if (res.code === 200 && res.rows) {
-        console.log('后端返回的商品数据:', res.rows)
+        console.log('后端返回的商品数据:', res.rows);
         const products = res.rows.map((p: any) => ({
           image: p.productImage || '',
           name: p.productName || '',
@@ -264,104 +381,109 @@ const handleExpand = async (order: any) => {
           spec2: p.productNo || '',
           price: p.orderPrice || 0,
           quantity: p.orderQuantity || 0
-        }))
-        console.log('商品加载完成, products.length:', products.length)
+        }));
+        console.log('商品加载完成, products.length:', products.length);
 
         // 替换整个数组以触发响应式更新
-        allOrders.value = allOrders.value.map((o, i) =>
-          i === orderIndex ? { ...o, expanded: true, products } : o
-        )
-        console.log('已更新数组,expanded 设置为 true')
-        return
+        allOrders.value = allOrders.value.map((o, i) => (i === orderIndex ? { ...o, expanded: true, products } : o));
+        console.log('已更新数组,expanded 设置为 true');
+        return;
       }
     } catch (error) {
-      console.error('加载商品失败:', error)
-      return
+      console.error('加载商品失败:', error);
+      return;
     }
   }
 
-  console.log('切换 expanded 状态, 从', currentOrder.expanded, '到', !currentOrder.expanded)
+  console.log('切换 expanded 状态, 从', currentOrder.expanded, '到', !currentOrder.expanded);
   // 替换整个数组以触发响应式更新
-  allOrders.value = allOrders.value.map((o, i) =>
-    i === orderIndex ? { ...o, expanded: !o.expanded } : o
-  )
-  console.log('已更新数组,expanded 已切换')
-}
-const handleSelectAll = (val: CheckboxValueType) => { orderList.value.forEach(order => { order.checked = !!val }) }
-const handleOrderCheck = () => { selectAll.value = orderList.value.every(order => order.checked) }
-const handleViewDetail = (order: any) => { router.push(`/order/orderManage/detail/${order.orderNo}`) }
+  allOrders.value = allOrders.value.map((o, i) => (i === orderIndex ? { ...o, expanded: !o.expanded } : o));
+  console.log('已更新数组,expanded 已切换');
+};
+const handleSelectAll = (val: CheckboxValueType) => {
+  orderList.value.forEach((order) => {
+    order.checked = !!val;
+  });
+};
+const handleOrderCheck = () => {
+  selectAll.value = orderList.value.every((order) => order.checked);
+};
+const handleViewDetail = (order: any) => {
+  router.push(`/order/orderManage/detail/${order.orderNo}`);
+};
 const handleQuery = () => {
-  queryParams.pageNum = 1
-  fetchOrderList()
-}
+  queryParams.pageNum = 1;
+  fetchOrderList();
+};
 const handleReset = () => {
-  queryParams.keyword = ''
-  queryParams.dateRange = null
-  queryParams.department = ''
-  queryParams.payType = ''
-  queryParams.pageNum = 1
-  fetchOrderList()
-}
+  queryParams.keyword = '';
+  queryParams.dateRange = null;
+  queryParams.department = '';
+  queryParams.payType = '';
+  queryParams.pageNum = 1;
+  fetchOrderList();
+};
 const handleAction = (action: string, order: any) => {
   switch (action) {
     case '取消订单':
-      handleCancelOrder(order)
-      break
+      handleCancelOrder(order);
+      break;
     case '删除订单':
-      handleDeleteOrder(order)
-      break
+      handleDeleteOrder(order);
+      break;
     case '查看详情':
-      handleViewDetail(order)
-      break
+      handleViewDetail(order);
+      break;
     default:
-      ElMessage.info(`${action}功能开发中`)
+      ElMessage.info(`${action}功能开发中`);
   }
-}
+};
 const handleCancelOrder = async (order: any) => {
   try {
     await ElMessageBox.confirm('确定要取消该订单吗?', '提示', {
       confirmButtonText: '确定',
       cancelButtonText: '取消',
       type: 'warning'
-    })
-    const res = await cancelOrder({ id: order.id, orderStatus: '7' })
+    });
+    const res = await cancelOrder({ id: order.id, orderStatus: '7' });
     if (res.code === 200) {
-      ElMessage.success('订单已取消')
-      fetchOrderList()
+      ElMessage.success('订单已取消');
+      fetchOrderList();
     } else {
-      ElMessage.error(res.msg || '取消订单失败')
+      ElMessage.error(res.msg || '取消订单失败');
     }
   } catch (error: any) {
     if (error !== 'cancel') {
-      ElMessage.error('取消订单失败')
+      ElMessage.error('取消订单失败');
     }
   }
-}
+};
 const handleDeleteOrder = async (order: any) => {
   try {
     await ElMessageBox.confirm('确定要删除该订单吗?删除后无法恢复。', '提示', {
       confirmButtonText: '确定',
       cancelButtonText: '取消',
       type: 'warning'
-    })
-    const res = await deleteOrder([order.id])
+    });
+    const res = await deleteOrder([order.id]);
     if (res.code === 200) {
-      ElMessage.success('订单已删除')
-      fetchOrderList()
+      ElMessage.success('订单已删除');
+      fetchOrderList();
     } else {
-      ElMessage.error(res.msg || '删除订单失败')
+      ElMessage.error(res.msg || '删除订单失败');
     }
   } catch (error: any) {
     if (error !== 'cancel') {
-      ElMessage.error('删除订单失败')
+      ElMessage.error('删除订单失败');
     }
   }
-}
+};
 
 // 页面加载时获取订单列表
 onMounted(() => {
-  fetchOrderList()
-})
+  loadDeptTree();
+  fetchOrderList();
+});
 </script>
 
 <style scoped lang="scss">
@@ -373,16 +495,217 @@ onMounted(() => {
   flex-direction: column;
   max-height: calc(100vh - 120px); // 减去顶部导航栏和其他元素高度
 }
-.page-title { font-size: 16px; font-weight: bold; display: flex; align-items: center; gap: 8px; margin-bottom: 20px; }
-.title-bar { display: inline-block; width: 3px; height: 16px; background: #e60012; border-radius: 2px; }
-.search-bar { display: flex; align-items: center; gap: 15px; margin-bottom: 15px; }
-.filter-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 15px; .filter-label { font-size: 14px; color: #666; } .filter-right { flex: 1; display: flex; justify-content: flex-end; gap: 10px; } }
-.tab-bar { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #eee; margin-bottom: 15px; .tab-left { display: flex; gap: 25px; } .tab-item { display: flex; align-items: center; gap: 5px; padding: 12px 0; cursor: pointer; color: #666; font-size: 14px; border-bottom: 2px solid transparent; margin-bottom: -1px; &:hover, &.active { color: #333; } &.active { border-bottom-color: #e60012; } } }
+.page-title {
+  font-size: 16px;
+  font-weight: bold;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  margin-bottom: 20px;
+}
+.title-bar {
+  display: inline-block;
+  width: 3px;
+  height: 16px;
+  background: #e60012;
+  border-radius: 2px;
+}
+.search-bar {
+  display: flex;
+  align-items: center;
+  gap: 15px;
+  margin-bottom: 15px;
+}
+.filter-bar {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-bottom: 15px;
+  .filter-label {
+    font-size: 14px;
+    color: #666;
+  }
+  .filter-right {
+    flex: 1;
+    display: flex;
+    justify-content: flex-end;
+    gap: 10px;
+  }
+}
+.tab-bar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border-bottom: 1px solid #eee;
+  margin-bottom: 15px;
+  .tab-left {
+    display: flex;
+    gap: 25px;
+  }
+  .tab-item {
+    display: flex;
+    align-items: center;
+    gap: 5px;
+    padding: 12px 0;
+    cursor: pointer;
+    color: #666;
+    font-size: 14px;
+    border-bottom: 2px solid transparent;
+    margin-bottom: -1px;
+    &:hover,
+    &.active {
+      color: #333;
+    }
+    &.active {
+      border-bottom-color: #e60012;
+    }
+  }
+}
 .order-list {
   flex: 1;
   overflow-y: auto;
   margin-bottom: 15px;
-  .order-card { border: 1px solid #eee; border-radius: 4px; margin-bottom: 15px; overflow: hidden; .order-header { display: flex; align-items: center; gap: 15px; padding: 12px 15px; background: #f9f9f9; border-bottom: 1px solid #eee; font-size: 13px; color: #666; .order-time { color: #333; } .expand-btn { margin-left: auto; } } .countdown-bar { background: #fff5e6; color: #e6a23c; padding: 8px 15px; font-size: 13px; border-bottom: 1px solid #eee; } .product-list { .product-row { display: flex; border-bottom: 1px solid #f5f5f5; &:last-child { border-bottom: none; } } .product-cell { padding: 15px; display: flex; flex-direction: column; justify-content: center; } .product-info-cell { flex: 1; flex-direction: row; align-items: center; gap: 15px; .product-image { width: 80px; height: 80px; background: #f5f5f5; border-radius: 4px; overflow: hidden; flex-shrink: 0; .el-image { width: 100%; height: 100%; } .image-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; } } .product-detail { flex: 1; .product-name { font-size: 14px; color: #333; margin-bottom: 5px; line-height: 1.4; } .product-spec { font-size: 12px; color: #999; margin-bottom: 5px; } .product-price { font-size: 16px; font-weight: bold; color: #e60012; } } .product-quantity { font-size: 13px; color: #666; } } .amount-cell { width: 120px; border-left: 1px solid #f5f5f5; .amount-info { margin-bottom: 5px; .label { font-size: 12px; color: #999; } .value { font-size: 14px; color: #333; &.highlight { font-size: 16px; font-weight: bold; color: #e60012; } } } } .status-cell { width: 120px; border-left: 1px solid #f5f5f5; align-items: flex-start; gap: 5px; .status-text { font-size: 14px; font-weight: 500; } .audit-status { font-size: 12px; &.success { color: #e60012; } &.warning { color: #e6a23c; } &.danger { color: #e60012; } } } .action-cell { width: 100px; border-left: 1px solid #f5f5f5; align-items: flex-start; gap: 3px; } } } }
+  .order-card {
+    border: 1px solid #eee;
+    border-radius: 4px;
+    margin-bottom: 15px;
+    overflow: hidden;
+    .order-header {
+      display: flex;
+      align-items: center;
+      gap: 15px;
+      padding: 12px 15px;
+      background: #f9f9f9;
+      border-bottom: 1px solid #eee;
+      font-size: 13px;
+      color: #666;
+      .order-time {
+        color: #333;
+      }
+      .expand-btn {
+        margin-left: auto;
+      }
+    }
+    .countdown-bar {
+      background: #fff5e6;
+      color: #e6a23c;
+      padding: 8px 15px;
+      font-size: 13px;
+      border-bottom: 1px solid #eee;
+    }
+    .product-list {
+      .product-row {
+        display: flex;
+        border-bottom: 1px solid #f5f5f5;
+        &:last-child {
+          border-bottom: none;
+        }
+      }
+      .product-cell {
+        padding: 15px;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+      }
+      .product-info-cell {
+        flex: 1;
+        flex-direction: row;
+        align-items: center;
+        gap: 15px;
+        .product-image {
+          width: 80px;
+          height: 80px;
+          background: #f5f5f5;
+          border-radius: 4px;
+          overflow: hidden;
+          flex-shrink: 0;
+          .el-image {
+            width: 100%;
+            height: 100%;
+          }
+          .image-placeholder {
+            width: 100%;
+            height: 100%;
+            display: flex;
+            align-items: center;
+            justify-content: center;
+          }
+        }
+        .product-detail {
+          flex: 1;
+          .product-name {
+            font-size: 14px;
+            color: #333;
+            margin-bottom: 5px;
+            line-height: 1.4;
+          }
+          .product-spec {
+            font-size: 12px;
+            color: #999;
+            margin-bottom: 5px;
+          }
+          .product-price {
+            font-size: 16px;
+            font-weight: bold;
+            color: #e60012;
+          }
+        }
+        .product-quantity {
+          font-size: 13px;
+          color: #666;
+        }
+      }
+      .amount-cell {
+        width: 120px;
+        border-left: 1px solid #f5f5f5;
+        .amount-info {
+          margin-bottom: 5px;
+          .label {
+            font-size: 12px;
+            color: #999;
+          }
+          .value {
+            font-size: 14px;
+            color: #333;
+            &.highlight {
+              font-size: 16px;
+              font-weight: bold;
+              color: #e60012;
+            }
+          }
+        }
+      }
+      .status-cell {
+        width: 120px;
+        border-left: 1px solid #f5f5f5;
+        align-items: flex-start;
+        gap: 5px;
+        .status-text {
+          font-size: 14px;
+          font-weight: 500;
+        }
+        .audit-status {
+          font-size: 12px;
+          &.success {
+            color: #e60012;
+          }
+          &.warning {
+            color: #e6a23c;
+          }
+          &.danger {
+            color: #e60012;
+          }
+        }
+      }
+      .action-cell {
+        width: 100px;
+        border-left: 1px solid #f5f5f5;
+        align-items: flex-start;
+        gap: 3px;
+      }
+    }
+  }
+}
 .bottom-bar {
   background: #fafafa;
   border: 1px solid #eee;
@@ -392,7 +715,20 @@ onMounted(() => {
   justify-content: space-between;
   align-items: center;
   flex-shrink: 0;
-  .bottom-right { display: flex; align-items: center; gap: 15px; .selected-info { font-size: 14px; color: #666; em { color: #e60012; font-style: normal; font-weight: bold; } } }
+  .bottom-right {
+    display: flex;
+    align-items: center;
+    gap: 15px;
+    .selected-info {
+      font-size: 14px;
+      color: #666;
+      em {
+        color: #e60012;
+        font-style: normal;
+        font-weight: bold;
+      }
+    }
+  }
 }
 :deep(.table-pagination) {
   flex-shrink: 0;

+ 81 - 61
src/views/organization/deptManage/index.vue

@@ -40,7 +40,16 @@
     <el-dialog v-model="dialogVisible" :title="dialogTitle" width="500px" destroy-on-close>
       <el-form ref="formRef" :model="formData" :rules="formRules" label-width="100px">
         <el-form-item label="上级部门">
-          <el-input :value="formData.parentName || '无(顶级部门)'" disabled />
+          <el-tree-select
+            v-model="formData.parentId"
+            :data="deptList"
+            :props="{ value: 'deptId', label: 'deptName', children: 'children' }"
+            value-key="deptId"
+            placeholder="请选择"
+            check-strictly
+            :render-after-expand="false"
+            style="width: 100%"
+          />
         </el-form-item>
         <el-form-item label="部门名称" prop="deptName">
           <el-input v-model="formData.deptName" placeholder="请输入部门名称" />
@@ -64,15 +73,19 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { PageTitle } from '@/components'
-import { getDeptTree, addDept, updateDept, deleteDept } from '@/api/pc/organization'
+import { ref, reactive, computed, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { PageTitle } from '@/components';
+import { getDeptTree, addDept, updateDept, deleteDept } from '@/api/pc/organization';
+import { DeptInfo } from '@/api/pc/organization/types';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const dialogVisible = ref(false)
-const formRef = ref()
-const editingRow = ref<any>(null)
-const parentRow = ref<any>(null)
+const { sys_platform_yes_no } = toRefs<any>(proxy?.useDict('sys_platform_yes_no'));
+
+const dialogVisible = ref(false);
+const formRef = ref();
+const editingRow = ref<any>(null);
+const parentRow = ref<any>(null);
 
 const formData = reactive({
   deptId: undefined,
@@ -81,52 +94,59 @@ const formData = reactive({
   status: '0',
   yearlyBudget: 0,
   parentName: ''
-})
+});
 
 const formRules = {
   deptName: [{ required: true, message: '请输入部门名称', trigger: 'blur' }]
-}
+};
 
-const dialogTitle = computed(() => editingRow.value ? '编辑部门' : '新增部门')
+const dialogTitle = computed(() => (editingRow.value ? '编辑部门' : '新增部门'));
 
-const deptList = ref([])
+const deptList = ref([]);
 
 // 加载部门树
 const loadDeptTree = async () => {
   try {
-    const res = await getDeptTree()
+    const res = await getDeptTree();
     if (res.code === 200 && res.data) {
-      deptList.value = res.data
+      deptList.value = res.data;
+
+      if (Array.isArray(res.data)) {
+        const treeData = proxy?.handleTree<DeptInfo>(res.data, 'deptId', 'parentId');
+        deptList.value = treeData || res.data;
+      } else {
+        deptList.value = [];
+      }
     }
   } catch (error) {
-    console.error('获取部门树失败:', error)
-    ElMessage.error('获取部门树失败')
+    console.error('获取部门树失败:', error);
+    ElMessage.error('获取部门树失败');
   }
-}
+};
 
 const handleAdd = (parent: any) => {
-  editingRow.value = null
-  parentRow.value = parent
-  formData.deptId = undefined
-  formData.deptName = ''
-  formData.parentId = parent ? parent.deptId : undefined
-  formData.status = '0'
-  formData.yearlyBudget = 0
-  formData.parentName = parent ? parent.deptName : ''
-  dialogVisible.value = true
-}
+  editingRow.value = null;
+  parentRow.value = parent;
+  formData.deptId = undefined;
+  formData.deptName = '';
+  formData.parentId = parent ? parent.deptId : undefined;
+  formData.status = '0';
+  formData.yearlyBudget = 0;
+  formData.parentName = parent ? parent.parentName : '';
+  dialogVisible.value = true;
+};
 
 const handleEdit = (row: any) => {
-  editingRow.value = row
-  parentRow.value = null
-  formData.deptId = row.deptId
-  formData.deptName = row.deptName
-  formData.parentId = row.parentId
-  formData.status = row.status || '0'
-  formData.yearlyBudget = row.yearlyBudget || 0
-  formData.parentName = ''
-  dialogVisible.value = true
-}
+  editingRow.value = row;
+  parentRow.value = null;
+  formData.deptId = row.deptId;
+  formData.deptName = row.deptName;
+  formData.parentId = row.parentId;
+  formData.status = row.status || '0';
+  formData.yearlyBudget = row.yearlyBudget || 0;
+  formData.parentName = '';
+  dialogVisible.value = true;
+};
 
 const handleDelete = (row: any) => {
   ElMessageBox.confirm(`确定要删除部门"${row.deptName}"吗?`, '提示', {
@@ -135,19 +155,19 @@ const handleDelete = (row: any) => {
     type: 'warning'
   }).then(async () => {
     try {
-      await deleteDept([row.deptId])
-      ElMessage.success('删除成功')
-      await loadDeptTree()
+      await deleteDept([row.deptId]);
+      ElMessage.success('删除成功');
+      await loadDeptTree();
     } catch (error) {
-      console.error('删除失败:', error)
-      ElMessage.error('删除失败')
+      console.error('删除失败:', error);
+      ElMessage.error('删除失败');
     }
-  })
-}
+  });
+};
 
 const handleSubmit = async () => {
-  const valid = await formRef.value?.validate()
-  if (!valid) return
+  const valid = await formRef.value?.validate();
+  if (!valid) return;
 
   try {
     const data = {
@@ -156,28 +176,28 @@ const handleSubmit = async () => {
       parentId: formData.parentId,
       status: formData.status,
       yearlyBudget: formData.yearlyBudget
-    }
+    };
 
     if (editingRow.value) {
-      await updateDept(data)
-      ElMessage.success('编辑成功')
+      await updateDept(data);
+      ElMessage.success('编辑成功');
     } else {
-      await addDept(data)
-      ElMessage.success('新增成功')
+      await addDept(data);
+      ElMessage.success('新增成功');
     }
 
-    dialogVisible.value = false
-    await loadDeptTree()
+    dialogVisible.value = false;
+    await loadDeptTree();
   } catch (error) {
-    console.error('保存失败:', error)
-    ElMessage.error('保存失败')
+    console.error('保存失败:', error);
+    ElMessage.error('保存失败');
   }
-}
+};
 
 // 页面加载时获取部门树
 onMounted(() => {
-  loadDeptTree()
-})
+  loadDeptTree();
+});
 </script>
 
 <style scoped lang="scss">
@@ -186,7 +206,7 @@ onMounted(() => {
   justify-content: space-between;
   align-items: center;
   margin-bottom: 20px;
-  
+
   :deep(.page-title) {
     margin-bottom: 0;
   }
@@ -198,7 +218,7 @@ onMounted(() => {
     font-weight: 500;
     color: #333;
   }
-  
+
   .el-table__expand-icon {
     color: #666;
     font-size: 12px;

+ 57 - 57
src/views/organization/roleManage/index.vue

@@ -68,17 +68,17 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed, onMounted } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { PageTitle } from '@/components'
-import { getRoleList, addRole, updateRole, deleteRole } from '@/api/pc/organization'
-
-const roleList = ref([])
-const currentPage = ref(1)
-const pageSize = ref(10)
-const dialogVisible = ref(false)
-const formRef = ref()
-const editingRow = ref<any>(null)
+import { ref, reactive, computed, onMounted } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { PageTitle } from '@/components';
+import { getRoleList, addRole, updateRole, deleteRole } from '@/api/pc/organization';
+
+const roleList = ref([]);
+const currentPage = ref(1);
+const pageSize = ref(10);
+const dialogVisible = ref(false);
+const formRef = ref();
+const editingRow = ref<any>(null);
 
 const formData = reactive({
   roleId: undefined,
@@ -87,45 +87,45 @@ const formData = reactive({
   roleSort: 0,
   status: '0',
   remark: ''
-})
+});
 
 const formRules = {
   roleName: [{ required: true, message: '请输入角色名称', trigger: 'blur' }],
   roleKey: [{ required: true, message: '请输入权限字符', trigger: 'blur' }]
-}
+};
 
-const total = computed(() => roleList.value.length)
-const dialogTitle = computed(() => editingRow.value ? '编辑角色' : '新增角色')
+const total = computed(() => roleList.value.length);
+const dialogTitle = computed(() => (editingRow.value ? '编辑角色' : '新增角色'));
 
 const paginatedRoleList = computed(() => {
-  const start = (currentPage.value - 1) * pageSize.value
-  const end = start + pageSize.value
-  return roleList.value.slice(start, end)
-})
+  const start = (currentPage.value - 1) * pageSize.value;
+  const end = start + pageSize.value;
+  return roleList.value.slice(start, end);
+});
 
 const loadRoleList = async () => {
   try {
-    const res = await getRoleList()
+    const res = await getRoleList();
     if (res.code === 200 && res.data) {
-      roleList.value = res.data
+      roleList.value = res.data;
     }
   } catch (error) {
-    console.error('获取角色列表失败:', error)
-    ElMessage.error('获取角色列表失败')
+    console.error('获取角色列表失败:', error);
+    ElMessage.error('获取角色列表失败');
   }
-}
+};
 
 const handleSizeChange = (val: number) => {
-  pageSize.value = val
-  currentPage.value = 1
-}
+  pageSize.value = val;
+  currentPage.value = 1;
+};
 
 const handleCurrentChange = (val: number) => {
-  currentPage.value = val
-}
+  currentPage.value = val;
+};
 
 const handleAdd = () => {
-  editingRow.value = null
+  editingRow.value = null;
   Object.assign(formData, {
     roleId: undefined,
     roleName: '',
@@ -133,12 +133,12 @@ const handleAdd = () => {
     roleSort: 0,
     status: '0',
     remark: ''
-  })
-  dialogVisible.value = true
-}
+  });
+  dialogVisible.value = true;
+};
 
 const handleEdit = (row: any) => {
-  editingRow.value = row
+  editingRow.value = row;
   Object.assign(formData, {
     roleId: row.roleId,
     roleName: row.roleName,
@@ -146,29 +146,29 @@ const handleEdit = (row: any) => {
     roleSort: row.roleSort,
     status: row.status,
     remark: row.remark
-  })
-  dialogVisible.value = true
-}
+  });
+  dialogVisible.value = true;
+};
 
 const handleSubmit = async () => {
-  const valid = await formRef.value?.validate()
-  if (!valid) return
+  const valid = await formRef.value?.validate();
+  if (!valid) return;
 
   try {
     if (editingRow.value) {
-      await updateRole(formData)
-      ElMessage.success('编辑成功')
+      await updateRole(formData);
+      ElMessage.success('编辑成功');
     } else {
-      await addRole(formData)
-      ElMessage.success('新增成功')
+      await addRole(formData);
+      ElMessage.success('新增成功');
     }
-    dialogVisible.value = false
-    await loadRoleList()
+    dialogVisible.value = false;
+    await loadRoleList();
   } catch (error) {
-    console.error('操作失败:', error)
-    ElMessage.error('操作失败')
+    console.error('操作失败:', error);
+    ElMessage.error('操作失败');
   }
-}
+};
 
 const handleDelete = (row: any) => {
   ElMessageBox.confirm(`确定要删除角色"${row.roleName}"吗?`, '提示', {
@@ -177,19 +177,19 @@ const handleDelete = (row: any) => {
     type: 'warning'
   }).then(async () => {
     try {
-      await deleteRole([row.roleId])
-      ElMessage.success('删除成功')
-      await loadRoleList()
+      await deleteRole([row.roleId]);
+      ElMessage.success('删除成功');
+      await loadRoleList();
     } catch (error) {
-      console.error('删除失败:', error)
-      ElMessage.error('删除失败')
+      console.error('删除失败:', error);
+      ElMessage.error('删除失败');
     }
-  })
-}
+  });
+};
 
 onMounted(() => {
-  loadRoleList()
-})
+  loadRoleList();
+});
 </script>
 
 <style scoped lang="scss">

+ 118 - 94
src/views/organization/staffManage/index.vue

@@ -1,20 +1,20 @@
 <template>
   <div class="page-container staff-manage">
     <PageTitle title="人员管理" />
-    
+
     <div class="main-content">
       <!-- 左侧部门树 -->
       <div class="dept-tree">
         <el-tree
           :data="deptTreeData"
-          :props="{ label: 'name', children: 'children' }"
-          node-key="id"
+          :props="{ label: 'deptName', children: 'children' }"
+          node-key="deptId"
           default-expand-all
           highlight-current
           @node-click="handleDeptClick"
         >
           <template #default="{ node, data }">
-            <span :class="['tree-node', { active: currentDeptId === data.id }]">{{ node.label }}</span>
+            <span :class="['tree-node', { active: currentDeptId === data.deptId }]">{{ node.label }}</span>
           </template>
         </el-tree>
       </div>
@@ -23,7 +23,7 @@
       <div class="staff-list">
         <!-- 操作栏 -->
         <div class="action-bar">
-          <el-button type="danger" @click="handleAdd">+ 新增</el-button>
+          <!-- <el-button type="danger" @click="handleAdd">+ 新增</el-button> -->
           <el-button :disabled="selectedRows.length === 0" @click="handleBatchDelete">批量删除</el-button>
         </div>
 
@@ -62,19 +62,24 @@
           <el-input v-model="formData.phone" placeholder="请输入手机号" />
         </el-form-item>
         <el-form-item label="所属部门" prop="deptId">
-          <el-tree-select v-model="formData.deptId" :data="deptTreeData" :props="{ label: 'name', children: 'children' }" node-key="id" placeholder="请选择部门" style="width: 100%" />
+          <el-tree-select
+            v-model="formData.deptId"
+            :data="deptTreeData"
+            :props="{ label: 'deptName', children: 'children' }"
+            node-key="deptId"
+            placeholder="请选择部门"
+            style="width: 100%"
+          />
         </el-form-item>
         <el-form-item label="角色" prop="role">
           <el-select v-model="formData.role" placeholder="请选择角色" style="width: 100%">
-            <el-option label="采购负责人" value="采购负责人" />
-            <el-option label="普通员工" value="普通员工" />
-            <el-option label="部门主管" value="部门主管" />
+            <el-option v-for="role in roleList" :key="role.roleId" :label="role.roleName" :value="role.roleId.toString()" />
           </el-select>
         </el-form-item>
         <el-form-item label="状态">
           <el-radio-group v-model="formData.status">
-            <el-radio label="启用">启用</el-radio>
-            <el-radio label="停用">停用</el-radio>
+            <el-radio label="0">启用</el-radio>
+            <el-radio label="1">停用</el-radio>
           </el-radio-group>
         </el-form-item>
       </el-form>
@@ -87,18 +92,22 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, computed } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { PageTitle, TablePagination } from '@/components'
+import { ref, reactive, computed } from 'vue';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { PageTitle, TablePagination } from '@/components';
+import { DeptInfo } from '@/api/pc/organization/types';
+import { getDeptTree, getRoleList } from '@/api/pc/organization';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const currentDeptId = ref<number | null>(null);
+const dialogVisible = ref(false);
+const formRef = ref();
+const editingRow = ref<any>(null);
+const selectedRows = ref<any[]>([]);
 
-const currentDeptId = ref<number | null>(null)
-const dialogVisible = ref(false)
-const formRef = ref()
-const editingRow = ref<any>(null)
-const selectedRows = ref<any[]>([])
+const queryParams = reactive({ pageNum: 1, pageSize: 10 });
+const total = ref(50);
 
-const queryParams = reactive({ pageNum: 1, pageSize: 10 })
-const total = ref(50)
+const deptTreeData = ref<DeptInfo[]>([]);
 
 const formData = reactive({
   name: '',
@@ -106,81 +115,84 @@ const formData = reactive({
   deptId: null as number | null,
   role: '',
   status: '启用'
-})
+});
 
 const formRules = {
   name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
   phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
   deptId: [{ required: true, message: '请选择部门', trigger: 'change' }]
-}
+};
 
-const dialogTitle = computed(() => editingRow.value ? '编辑人员' : '新增人员')
-
-// 部门树数据
-const deptTreeData = ref([
-  {
-    id: 1, name: '人事部',
-    children: [
-      { id: 11, name: '人事部01' },
-      { id: 12, name: '人事部02' }
-    ]
-  },
-  {
-    id: 2, name: '技术部',
-    children: [
-      { id: 21, name: '技术部01' },
-      { id: 22, name: '技术部02' }
-    ]
-  },
-  { id: 3, name: '财务部' }
-])
+const dialogTitle = computed(() => (editingRow.value ? '编辑人员' : '新增人员'));
 
 // 人员列表数据
-const staffList = ref([
-  { id: 1, name: '郑婷婷', phone: '137 8651 5262', role: '采购负责人', deptName: '技术部', status: '启用' },
-  { id: 2, name: '冯艺莲', phone: '156 6084 7617', role: '采购负责人', deptName: '技术部', status: '启用' },
-  { id: 3, name: '吴彦琛', phone: '158 0598 5923', role: '采购负责人', deptName: '财务部', status: '启用' },
-  { id: 4, name: '吴彦漆', phone: '137 8231 8088', role: '采购负责人', deptName: '设计部', status: '启用' },
-  { id: 5, name: '周雄', phone: '180 6243 3980', role: '采购负责人', deptName: '财务部', status: '启用' },
-  { id: 6, name: '吴誉誉', phone: '131 1251 9348', role: '采购负责人', deptName: '人事部', status: '启用' },
-  { id: 7, name: '王凡玄', phone: '185 3264 9149', role: '采购负责人', deptName: '财务部', status: '启用' },
-  { id: 8, name: '冯启彬', phone: '184 5857 8572', role: '采购负责人', deptName: '设计部', status: '启用' },
-  { id: 9, name: '郑文锦', phone: '189 2860 9388', role: '采购负责人', deptName: '财务部', status: '启用' },
-  { id: 10, name: '赵吾光', phone: '181 0834 1643', role: '采购负责人', deptName: '人事部', status: '启用' }
-])
+const staffList = ref([]);
+const roleList = ref<any[]>([]);
+
+// 加载部门树
+const loadDeptTree = async () => {
+  try {
+    const res = await getDeptTree();
+    if (res.code === 200 && res.data) {
+      deptTreeData.value = res.data;
+
+      if (Array.isArray(res.data)) {
+        const treeData = proxy?.handleTree<DeptInfo>(res.data, 'deptId', 'parentId');
+        deptTreeData.value = treeData || res.data;
+      } else {
+        deptTreeData.value = [];
+      }
+    }
+  } catch (error) {
+    console.error('获取部门树失败:', error);
+    ElMessage.error('获取部门树失败');
+  }
+};
+
+// 加载角色列表
+const loadRoleList = async () => {
+  try {
+    const res = await getRoleList();
+    if (res.code === 200 && res.data) {
+      roleList.value = res.data;
+    }
+  } catch (error) {
+    console.error('获取角色列表失败:', error);
+  }
+};
 
 const handleDeptClick = (data: any) => {
-  currentDeptId.value = data.id
-  handleQuery()
-}
+  currentDeptId.value = data.id;
+  handleQuery();
+};
 
 const handleQuery = () => {
   // 根据选中的部门筛选人员
-}
+};
 
 const handleSelectionChange = (rows: any[]) => {
-  selectedRows.value = rows
-}
+  selectedRows.value = rows;
+};
 
 const handleAdd = () => {
-  editingRow.value = null
-  formData.name = ''
-  formData.phone = ''
-  formData.deptId = currentDeptId.value
-  formData.role = ''
-  formData.status = '启用'
-  dialogVisible.value = true
-}
+  editingRow.value = null;
+  formData.name = '';
+  formData.phone = '';
+  formData.deptId = currentDeptId.value;
+  formData.role = '';
+  formData.status = '启用';
+  dialogVisible.value = true;
+};
 
 const handleEdit = (row: any) => {
-  editingRow.value = row
-  formData.name = row.name
-  formData.phone = row.phone
-  formData.deptId = null
-  formData.role = row.role
-  formData.status = row.status
-  dialogVisible.value = true
-}
+  editingRow.value = row;
+  formData.name = row.name;
+  formData.phone = row.phone;
+  formData.deptId = null;
+  formData.role = row.role;
+  formData.status = row.status;
+  dialogVisible.value = true;
+};
 
 const handleBatchDelete = () => {
   ElMessageBox.confirm(`确定要删除选中的 ${selectedRows.value.length} 名人员吗?`, '提示', {
@@ -188,9 +200,9 @@ const handleBatchDelete = () => {
     cancelButtonText: '取消',
     type: 'warning'
   }).then(() => {
-    ElMessage.success('删除成功')
-  })
-}
+    ElMessage.success('删除成功');
+  });
+};
 
 const handleDelete = (row: any) => {
   ElMessageBox.confirm(`确定要删除"${row.name}"吗?`, '提示', {
@@ -198,24 +210,30 @@ const handleDelete = (row: any) => {
     cancelButtonText: '取消',
     type: 'warning'
   }).then(() => {
-    const index = staffList.value.findIndex(item => item.id === row.id)
+    const index = staffList.value.findIndex((item) => item.id === row.id);
     if (index > -1) {
-      staffList.value.splice(index, 1)
+      staffList.value.splice(index, 1);
     }
-    ElMessage.success('删除成功')
-  })
-}
+    ElMessage.success('删除成功');
+  });
+};
 
 const handleSubmit = async () => {
-  const valid = await formRef.value?.validate()
-  if (!valid) return
+  const valid = await formRef.value?.validate();
+  if (!valid) return;
   if (editingRow.value) {
-    ElMessage.success('编辑成功')
+    ElMessage.success('编辑成功');
   } else {
-    ElMessage.success('新增成功')
+    ElMessage.success('新增成功');
   }
-  dialogVisible.value = false
-}
+  dialogVisible.value = false;
+};
+
+// 页面加载时获取部门树
+onMounted(() => {
+  loadDeptTree();
+  loadRoleList();
+});
 </script>
 
 <style scoped lang="scss">
@@ -235,7 +253,9 @@ const handleSubmit = async () => {
     .tree-node {
       font-size: 14px;
       color: #333;
-      &.active { color: #e60012; }
+      &.active {
+        color: #e60012;
+      }
     }
 
     :deep(.el-tree-node__content) {
@@ -244,7 +264,9 @@ const handleSubmit = async () => {
 
     :deep(.el-tree-node.is-current > .el-tree-node__content) {
       background-color: #fff5f5;
-      .tree-node { color: #e60012; }
+      .tree-node {
+        color: #e60012;
+      }
     }
   }
 
@@ -261,7 +283,9 @@ const handleSubmit = async () => {
 
     .status-text {
       color: #999;
-      &.active { color: #67c23a; }
+      &.active {
+        color: #67c23a;
+      }
     }
   }
 }

+ 66 - 50
src/views/valueAdded/complaint/index.vue

@@ -1,44 +1,30 @@
 <template>
   <div class="complaint-container">
     <PageTitle title="投诉与建议" />
-    
-    <el-form :model="form" label-position="top" class="complaint-form">
+
+    <el-form :model="form" :rules="rules" label-position="top" class="complaint-form">
       <!-- 反馈类型 -->
       <el-form-item label="反馈类型">
-        <div class="type-tags">
-          <el-tag
+        <div class="tag-group">
+          <div
             v-for="item in feedbackTypes"
             :key="item.value"
-            :type="form.feedbackType === item.value ? 'danger' : 'info'"
-            :effect="form.feedbackType === item.value ? 'dark' : 'plain'"
-            class="type-tag"
+            :class="['tag-item', { active: form.feedbackType === item.value }]"
             @click="form.feedbackType = item.value"
           >
             {{ item.label }}
-          </el-tag>
+          </div>
         </div>
       </el-form-item>
 
       <!-- 问题和建议 -->
-      <el-form-item label="问题和建议">
-        <el-input
-          v-model="form.content"
-          type="textarea"
-          placeholder="请输入"
-          :rows="5"
-          maxlength="500"
-          show-word-limit
-        />
+      <el-form-item label="问题和建议" prop="feedbackContent">
+        <el-input v-model="form.feedbackContent" type="textarea" placeholder="请输入" :rows="5" maxlength="500" show-word-limit />
       </el-form-item>
 
       <!-- 上传照片 -->
       <el-form-item label="上传照片">
-        <el-upload
-          action="#"
-          list-type="picture-card"
-          :auto-upload="false"
-          :limit="5"
-        >
+        <el-upload action="#" list-type="picture-card" :auto-upload="false" :limit="5" @change="handleUploadChange">
           <el-icon><Plus /></el-icon>
           <template #tip>
             <div class="upload-tip">上传</div>
@@ -47,8 +33,8 @@
       </el-form-item>
 
       <!-- 联系方式 -->
-      <el-form-item label="联系方式">
-        <el-input v-model="form.contact" placeholder="请输入" style="width: 400px" />
+      <el-form-item label="联系方式" prop="phone">
+        <el-input v-model="form.phone" placeholder="请输入" style="width: 400px" />
       </el-form-item>
 
       <!-- 提交按钮 -->
@@ -60,37 +46,44 @@
 </template>
 
 <script setup lang="ts">
-import { reactive } from 'vue'
-import { Plus } from '@element-plus/icons-vue'
-import { ElMessage } from 'element-plus'
-import { PageTitle } from '@/components'
+import { reactive } from 'vue';
+import { Plus } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+import { PageTitle } from '@/components';
+import { addComplaintsSuggestions } from '@/api/pc/valueAdded';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { complaints_suggestion_type } = toRefs<any>(proxy?.useDict('complaints_suggestion_type'));
 
 // 反馈类型选项
-const feedbackTypes = [
-  { label: '功能建议', value: '1' },
-  { label: '客服问题', value: '2' },
-  { label: '库存与价格', value: '3' },
-  { label: '配送问题', value: '4' },
-  { label: '售后质量问题', value: '5' },
-  { label: '账款票问题', value: '6' },
-  { label: '其他', value: '7' }
-]
+const feedbackTypes = computed(() => complaints_suggestion_type.value || []);
 
 // 表单数据
 const form = reactive({
-  feedbackType: '1',
-  content: '',
-  contact: ''
-})
+  feedbackType: '0',
+  feedbackContent: '',
+  relatedPictures: '',
+  phone: ''
+});
+const rules = {
+  feedbackContent: [{ required: true, message: '请输入投诉建议内容', trigger: 'blur' }],
+  phone: [{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }]
+};
 
+// 上传图片
+const handleUploadChange = (file, fileList) => {};
 // 提交
-const handleSubmit = () => {
-  if (!form.content) {
-    ElMessage.warning('请输入问题和建议')
-    return
+const handleSubmit = async () => {
+  if (!form.feedbackContent) {
+    ElMessage.warning('请输入问题和建议');
+    return;
   }
-  ElMessage.success('提交成功')
-}
+  try {
+    await addComplaintsSuggestions(form);
+    ElMessage.success('提交成功');
+  } catch (error) {
+    ElMessage.error('提交失败');
+  }
+};
 </script>
 
 <style scoped lang="scss">
@@ -108,11 +101,11 @@ const handleSubmit = () => {
   display: flex;
   flex-wrap: wrap;
   gap: 10px;
-  
+
   .type-tag {
     cursor: pointer;
     border-radius: 4px;
-    
+
     &:hover {
       opacity: 0.8;
     }
@@ -134,4 +127,27 @@ const handleSubmit = () => {
   color: #999;
   text-align: center;
 }
+.tag-group {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 5px;
+  .tag-item {
+    padding: 5px 10px;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    cursor: pointer;
+    font-size: 10px;
+    color: #666;
+    transition: all 0.2s;
+    &:hover {
+      border-color: #e60012;
+      color: #e60012;
+    }
+    &.active {
+      border-color: #e60012;
+      color: #e60012;
+      background: #fff5f5;
+    }
+  }
+}
 </style>