瀏覽代碼

Merge branch 'xinyan' into dev

xinyan 7 月之前
父節點
當前提交
2d7c7c62ec

+ 1 - 1
src/utils/service.ts

@@ -177,7 +177,7 @@ function createRequestFunction(service: any) {
 			baseURL: getBaseURL(),
 			data: {},
 		};
-
+		delete config.headers
 		// const token = userStore.getToken;
 		const token = Session.get('token');
 		if (token != null) {

+ 48 - 0
src/views/computer-information/api.ts

@@ -1,8 +1,10 @@
 import { request } from '/@/utils/service';
+import { Session } from '/@/utils/storage';
 
 
 const apiPrefix = '/api/assets/computer/';
 
+// 卡片展示接口
 export function getCardData(query: any) {
   return request({
     url: apiPrefix + 'card/',
@@ -11,6 +13,7 @@ export function getCardData(query: any) {
   });
 }
 
+// 单条查询接口
 export function getComputerDetailOverview(id: number) {
   return request({
 		url: `${apiPrefix}${id}/`,
@@ -33,3 +36,48 @@ export function getPastTableData(query: any) {
   });
 }
 
+// 下拉框接口
+export function getShopList(query: any) {
+  return request({
+    url: '/api/assets/computer/shop/box/',
+    method: 'GET',
+    params: query,
+  });
+}
+
+export function getUserList(query: any) {
+  return request({
+    url: '/api/assets/people/box/',
+    method: 'GET',
+    params: query,
+  });
+}
+
+// 创建接口
+export function createComputer(body: any) {
+  return request({
+    url: apiPrefix + 'create/',
+    method: 'POST',
+    data: body,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+      // 'Authorization': 'JWT ' + Session.get('token')
+    }
+  });
+}
+
+// 更新接口
+export function updateComputerDetail(query: any) {
+  return request({
+    url: apiPrefix + `${query.id}/`,
+    method: 'POST',
+    params: { partial: query.partial },
+    data: query.dataToUpload,
+    headers: {
+      'Content-Type': 'multipart/form-data',
+    }
+  });
+}
+
+
+

+ 31 - 25
src/views/computer-information/components/ComputerDetail.vue

@@ -5,7 +5,7 @@
  * @Author: xinyan
  */
 import { useResponse } from '/@/utils/useResponse';
-import { ComputerColumns } from '/@/views/computer-information/useColumns';
+import { ComputerCurrentColumns, ComputerPastColumns } from '/@/views/computer-information/useColumns';
 import * as api from '../api';
 import { Picture as IconPicture } from '@element-plus/icons-vue';
 import { useTableData } from '/@/utils/useTableData';
@@ -13,9 +13,10 @@ import { usePagination } from '/@/utils/usePagination';
 
 const route = useRoute();
 const id = route.query.id;
+const computerNumber = route.query.computerNumber;
 const computerOverview: any = ref([]);
 const overviewLoading = ref();
-const currentView = ref('history');
+const currentView = ref('current');
 const { tableOptions, handlePageChange } = usePagination(fetchComputerData);
 
 const gridOptions: any = reactive({
@@ -47,7 +48,7 @@ const gridOptions: any = reactive({
 		icon: 'vxe-icon-indicator roll',
 		text: '正在拼命加载中...',
 	},
-	columns: ComputerColumns,
+	columns: ComputerCurrentColumns,
 	data: [],
 });
 
@@ -58,27 +59,32 @@ function pageChange({ pageSize, currentPage }: any) {
 }
 
 // 当前信息、历史记录
-async function fetchComputerData(view) {
-	if (view === currentView.value) {
-		return;
-	}
+async function fetchComputerData(view) { // 默认为当前视图
+  const query = {
+    page: gridOptions.pagerConfig.page,
+    limit: gridOptions.pagerConfig.limit,
+  };
 
-	const query = {
-		id: id,
-		page: gridOptions.pagerConfig.page,
-		limit: gridOptions.pagerConfig.limit,
-	};
+  switch (view) {
+    case 'current':
+      gridOptions.columns = ComputerCurrentColumns;
+      currentView.value = 'current';
+      query.computerNumber = computerNumber;
+      await useTableData(api.getCurrentTableData, query, gridOptions);
+      break;
+    case 'history':
+      gridOptions.columns = ComputerPastColumns;
+      currentView.value = 'history';
+      query.id = id;
+      await useTableData(api.getPastTableData, query, gridOptions);
+      break;
+  }
+}
 
-	switch (view) {
-		case 'current':
-			currentView.value = 'current';
-			await useTableData(api.getCurrentTableData, query, gridOptions);
-			break;
-		case 'history':
-			currentView.value = 'history';
-			await useTableData(api.getPastTableData, query, gridOptions);
-			break;
-	}
+function switchView(view) {
+  if (view !== currentView.value) { // 只有在不同的视图时才切换
+    fetchComputerData(view); // 调用 fetchComputerData 来加载新的视图数据
+  }
 }
 
 async function fetchComputerDetailOverview() {
@@ -108,7 +114,7 @@ const headerCellStyle = () => {
 };
 
 onMounted(() => {
-	fetchComputerData();
+	fetchComputerData(currentView.value);
 	fetchComputerDetailOverview();
 });
 </script>
@@ -167,8 +173,8 @@ onMounted(() => {
 		<el-card body-style="padding-top: 10px" class="mt-2.5" shadow="hover" style="border: none">
 			<vxe-grid :cell-style="cellStyle" :header-cell-style="headerCellStyle" v-bind="gridOptions">
 				<template #toolbar_buttons>
-					<el-button :type="currentView === 'current' ? 'primary' : 'default'" @click="fetchComputerData('current')"> 当前信息 </el-button>
-					<el-button :type="currentView === 'history' ? 'primary' : 'default'" @click="fetchComputerData('history')"> 历史记录 </el-button>
+					<el-button :type="currentView === 'current' ? 'primary' : 'default'" @click="switchView('current')"> 当前信息 </el-button>
+					<el-button :type="currentView === 'history' ? 'primary' : 'default'" @click="switchView('history')"> 历史记录 </el-button>
 				</template>
 				<template #pager>
 					<vxe-pager

+ 165 - 0
src/views/computer-information/components/CreateComputer.vue

@@ -0,0 +1,165 @@
+<script lang="ts" setup>
+/**
+ * @Name: CreateComputer.vue
+ * @Description:
+ * @Author: xinyan
+ */
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '../api';
+
+const showDialog = defineModel({ default: false });
+const emit = defineEmits([ 'refresh' ]);
+
+const { shopOptions, userOptions } = defineProps<{
+  shopOptions: any;
+  userOptions: any;
+}>();
+
+// 图片处理
+const upload = ref<UploadInstance>();
+
+// 表单数据
+interface RuleForm {
+	computerNumber: string;
+	computerType: string;
+	station: string;
+	shop: string;
+	user: string;
+	macaddress: string;
+	ipaddress: string;
+	images: UploadFile[];
+}
+
+const ruleFormRef = ref<FormInstance>();
+const formData = reactive<RuleForm>({
+	computerNumber: '',
+	computerType: '',
+	station: '',
+	shop: '',
+	user: '',
+	macaddress: '',
+	ipaddress: '',
+	images: [],
+});
+
+const rules = reactive<FormRules<RuleForm>>({
+	computerNumber: [{ required: true, message: '请输入电脑编号', trigger: 'blur' }],
+	computerType: [{ required: true, message: '请输入电脑类型', trigger: 'blur' }],
+	station: [{ required: true, message: '请输入工位号', trigger: 'blur' }],
+	shop: [{ required: true, message: '请输入所属商铺', trigger: 'blur' }],
+	user: [{ required: true, message: '请输入电脑使用人', trigger: 'blur' }],
+	macaddress: [{ required: true, message: '请输入MAC地址', trigger: 'blur' }],
+	ipaddress: [{ required: true, message: '请输入IP地址', trigger: 'blur' }],
+});
+
+// 超过图片限制时触发的回调
+const handleExceed: UploadProps['onExceed'] = (files) => {
+	upload.value!.clearFiles();
+	const file = files[0] as UploadRawFile;
+	file.uid = genFileId();
+	upload.value!.handleStart(file);
+};
+
+const submitForm = async (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	await formEl.validate((valid, fields) => {
+		if (valid) {
+			handleSave();
+			showDialog.value = false;
+      emit('refresh');
+		} else {
+			ElMessage.error('添加失败');
+		}
+	});
+};
+
+// 保存操作
+async function handleSave() {
+	const bodyData = new FormData();
+	const fieldsToAppend = ['computerNumber', 'computerType', 'station', 'user', 'macaddress', 'ipaddress'];
+
+	fieldsToAppend.forEach((field) => {
+		bodyData.append(field, formData[field as keyof RuleForm]);
+	});
+
+	const shopValue = Array.isArray(formData.shop) ? formData.shop.join(',') : formData.shop;
+	bodyData.append('shops', shopValue);
+
+	if (formData.images.length > 0 && formData.images[0].raw) {
+		bodyData.append('images', formData.images[0].raw);
+	}
+	const resp = await useResponse(bodyData, api.createComputer);
+	if (resp.code === 2000) {
+		ElMessage.success('添加成功');
+	}
+}
+
+// 取消操作
+const handleCancel = () => {
+	showDialog.value = false;
+};
+
+onMounted(() => {});
+</script>
+
+<template>
+	<el-dialog v-model="showDialog" :close-on-click-modal="false" title="新增电脑信息" width="35%">
+		<el-form ref="ruleFormRef" :model="formData" :rules="rules" class="computer-info-form" label-width="auto" status-icon>
+			<el-form-item label="电脑编号:" prop="computerNumber">
+				<el-input v-model="formData.computerNumber" placeholder="请输入电脑编号" />
+			</el-form-item>
+			<el-form-item label="电脑类型:" prop="computerType">
+				<el-input v-model="formData.computerType" placeholder="请输入电脑类型" />
+			</el-form-item>
+			<el-form-item label="工位号:" prop="station">
+				<el-input v-model="formData.station" placeholder="请输入工位号" />
+			</el-form-item>
+			<el-form-item label="所属店铺:" prop="shop">
+				<el-select v-model="formData.shop" clearable collapse-tags filterable multiple placeholder="请选择店铺">
+					<el-option v-for="shop in shopOptions" :key="shop.value" :label="shop.label" :value="shop.value" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="电脑使用人:" prop="user">
+				<el-select v-model="formData.user" placeholder="请选择电脑使用人">
+					<el-option v-for="user in userOptions" :key="user.value" :label="user.label" :value="user.value" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="MAC地址:" prop="macaddress">
+				<el-input v-model="formData.macaddress" placeholder="请输入MAC地址" />
+			</el-form-item>
+			<el-form-item label="使用IP地址:" prop="ipaddress">
+				<el-input v-model="formData.ipaddress" placeholder="请输入IP地址" />
+			</el-form-item>
+			<!-- 电脑图片上传 -->
+			<el-form-item :key="images" label="电脑图片:">
+				<el-upload
+					ref="upload"
+					v-model:file-list="formData.images"
+					:auto-upload="false"
+					:limit="1"
+					:on-exceed="handleExceed"
+					action="#"
+					list-type="picture-card"
+				>
+					<el-icon>
+						<Plus />
+					</el-icon>
+				</el-upload>
+			</el-form-item>
+		</el-form>
+		<template #footer>
+			<div class="dialog-footer">
+				<el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button>
+				<el-button @click="handleCancel">取消</el-button>
+			</div>
+		</template>
+	</el-dialog>
+</template>
+
+<style scoped>
+.dialog-footer {
+	margin-bottom: 10px;
+	text-align: center;
+}
+</style>

+ 153 - 71
src/views/computer-information/components/EditComputerInfo.vue

@@ -5,14 +5,27 @@
  * @Author: xinyan
  */
 import { ref } from 'vue';
-import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+import { ElMessage, FormInstance, FormRules, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
 import { genFileId } from 'element-plus';
-import router from '/@/router';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '../api';
+import { EditPen, Picture as IconPicture, Plus, Search } from '@element-plus/icons-vue';
+
+const visible = defineModel({ default: false });
+const { computerInfo, computerNumber , userOptions, shopOptions } = defineProps<{
+  computerInfo: any;
+  computerNumber: any;
+  userOptions: any;
+  shopOptions: any;
+}>();
+
+const emit = defineEmits(['refresh']);
 
 // 图片处理
 const upload = ref<UploadInstance>();
 // 表单数据
-const formData = ref({
+const ruleFormRef = ref<FormInstance>();
+const formData = reactive<RuleForm>({
 	computerNumber: '',
 	computerType: '',
 	station: '',
@@ -22,28 +35,19 @@ const formData = ref({
 	ipaddress: '',
 	images: [],
 });
+const rules = reactive<FormRules<RuleForm>>({
+  computerNumber: [{ required: true, message: '请输入电脑编号', trigger: 'blur' }],
+  computerType: [{ required: true, message: '请输入电脑类型', trigger: 'blur' }],
+  station: [{ required: true, message: '请输入所在站点', trigger: 'blur' }],
+  shop: [{ required: true, message: '请输入所属商铺', trigger: 'blur' }],
+  user: [{ required: true, message: '请输入使用者', trigger: 'blur' }],
+  macaddress: [{ required: true, message: '请输入MAC地址', trigger: 'blur' }],
+  ipaddress: [{ required: true, message: '请输入IP地址', trigger: 'blur' }],
+});
 
-const shopOptions = [
-	{
-		value: 'shop1',
-		label: '店铺一',
-	},
-	{
-		value: 'shop2',
-		label: '店铺二',
-	},
-];
-
-// 表单字段配置
-const formFields = [
-	{ label: '电脑编号:', key: 'computerNumber', required: true },
-	{ label: '电脑类型:', key: 'computerType', required: true },
-	{ label: '工位号:', key: 'station', required: true },
-	{ label: '所属店铺:', key: 'shop', required: true },
-	{ label: '电脑使用人:', key: 'user', required: true },
-	{ label: 'MAC地址:', key: 'macaddress', required: true },
-	{ label: '使用IP地址:', key: 'ipaddress', required: true },
-];
+onBeforeMount(() => {
+  replaceCol();
+});
 
 // 超过图片限制时触发的回调
 const handleExceed: UploadProps['onExceed'] = (files) => {
@@ -54,57 +58,135 @@ const handleExceed: UploadProps['onExceed'] = (files) => {
 };
 
 // 保存操作
-const handleSave = () => {
-	console.log(formData);
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      const dataToUpload = new FormData();
+
+      // for (const key in formData) {
+      //   if (key !== 'images' && formData.hasOwnProperty(key)) {
+      //     dataToUpload.append(key, formData[key]);
+      //   }
+      // }
+
+      const fieldsToAppend = ['computerNumber', 'computerType', 'station', 'user', 'macaddress', 'ipaddress'];
+
+      fieldsToAppend.forEach((field) => {
+        dataToUpload.append(field, formData[field as keyof RuleForm]);
+      });
+
+      const shopValue = Array.isArray(formData.shop) ? formData.shop.join(',') : formData.shop;
+      dataToUpload.append('shops', shopValue);
+
+      if (Array.isArray(formData.images)) {
+        formData.images.forEach((image: any) => {
+          if (image.raw) {
+            dataToUpload.append('images', image.raw);
+          }
+        });
+      }
+      try {
+        const resp = await useResponse({ id: computerInfo.id, partial: 1, dataToUpload, }, api.updateComputerDetail);
+        // 检查上传结果并提示用户
+        if (resp.code === 2000) {
+          ElMessage.success('修改成功');
+          visible.value = false;
+          emit('refresh');
+        }
+      } catch (error) {
+        console.error('上传失败:', error);
+        ElMessage.error('上传失败,请重试');
+      }
+    } else {
+      console.log('error submit!', fields);
+    }
+  });
 };
 
-// 取消操作
-const handleCancel = () => {
-	router.push({
-		path: '/computer',
-	});
+const resetForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return;
+  formEl.resetFields();
 };
+
+function replaceCol() {
+  const result = Object.keys(formData).reduce((acc, key) => {
+    if (key in computerInfo) {
+      if (key === 'images' && Array.isArray(computerInfo[key])) {
+        acc[key] = computerInfo[key].map((img: { image: string }) => ({
+          name: '已上传图片',
+          url: img.image_url,
+        }));
+      } else {
+        acc[key] = computerInfo[key];
+      }
+    }
+    return acc;
+  }, {} as { [key: string]: any });
+  Object.assign(formData, result);
+  console.log(formData.images[0].url);
+}
+
+function handleImageError(file) {
+  // 当图片加载失败时,设置 file 对象的 error 状态为 true
+  file.error = true;
+}
 </script>
 
 <template>
-	<el-card class="m-3.5">
-		<div class="font-bold text-xl pb-3.5">电脑信息编辑</div>
-		<el-form :model="formData" class="computer-info-form" label-width="120px">
-			<!-- 使用 v-for 循环渲染表单项 -->
-			<el-form-item v-for="(field, index) in formFields" :key="index" :label="field.label" :required="field.required" size="large">
-				<!-- 判断所属店铺,渲染 el-select -->
-				<el-select v-if="field.key === 'shop'" v-model="formData[field.key]" placeholder="请选择店铺" style="width: 70%">
-					<el-option v-for="shop in shopOptions" :key="shop.value" :label="shop.label" :value="shop.value" />
-				</el-select>
-
-				<!-- 其他字段渲染 el-input -->
-				<el-input v-else v-model="formData[field.key]" style="width: 70%" />
-			</el-form-item>
-
-			<!-- 电脑图片上传 -->
-			<el-form-item :key="images" label="电脑图片:">
-				<el-upload
-					ref="upload"
-					v-model:file-list="formData.images"
-					:auto-upload="false"
-					:limit="1"
-					:on-exceed="handleExceed"
-					action="#"
-					list-type="picture-card"
-				>
-					<el-icon>
-						<Plus />
-					</el-icon>
-				</el-upload>
-			</el-form-item>
-
-			<!-- 按钮组 -->
-			<el-form-item class="button-group">
-				<el-button type="primary" @click="handleSave">保存</el-button>
-				<el-button @click="handleCancel">取消</el-button>
-			</el-form-item>
-		</el-form>
-	</el-card>
+	<el-drawer :title="`电脑编辑 - ${computerNumber}`" v-model="visible" size="30%">
+			<el-form  ref="ruleFormRef" :model="formData" :rules="rules" class="computer-info-form" label-width="auto">
+        <el-form-item label="电脑编号:" prop="computerNumber">
+          <el-input v-model="formData.computerNumber" placeholder="请输入电脑编号" />
+        </el-form-item>
+        <el-form-item label="电脑类型:" prop="computerType">
+          <el-input v-model="formData.computerType" placeholder="请输入电脑类型" />
+        </el-form-item>
+        <el-form-item label="工位号:" prop="station">
+          <el-input v-model="formData.station" placeholder="请输入工位号" />
+        </el-form-item>
+        <el-form-item label="所属店铺:" prop="shop">
+          <el-select v-model="formData.shop" clearable filterable multiple collapse-tags placeholder="请选择店铺">
+            <el-option v-for="shop in shopOptions" :key="shop.value" :label="shop.label" :value="shop.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="电脑使用人:" prop="user">
+          <el-select v-model="formData.user" placeholder="请选择电脑使用人">
+            <el-option v-for="user in userOptions" :key="user.value" :label="user.label" :value="user.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="MAC地址:" prop="macaddress">
+          <el-input v-model="formData.macaddress" placeholder="请输入MAC地址" />
+        </el-form-item>
+        <el-form-item label="使用IP地址:" prop="ipaddress">
+          <el-input v-model="formData.ipaddress" placeholder="请输入IP地址" />
+        </el-form-item>
+
+				<!-- 电脑图片上传 -->
+				<el-form-item :key="images" label="电脑图片:">
+					<el-upload
+						ref="upload"
+						v-model:file-list="formData.images"
+						:auto-upload="false"
+						:limit="1"
+						:on-exceed="handleExceed"
+						action="#"
+						list-type="picture-card"
+					>
+						<el-icon>
+							<Plus />
+						</el-icon>
+					</el-upload>
+				</el-form-item>
+
+				<!-- 按钮组 -->
+				<el-form-item class="button-group">
+          <el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button>
+          <el-button @click="resetForm(ruleFormRef)">取消</el-button>
+				</el-form-item>
+			</el-form>
+	</el-drawer>
 </template>
 
 <style scoped>
@@ -118,7 +200,7 @@ const handleCancel = () => {
 }
 
 .computer-info-form :deep(.el-form-item__label) {
-	font-size: 15px;
-	font-weight: bold;
+	font-size: 14px;
+	font-weight: 500;
 }
 </style>

+ 115 - 63
src/views/computer-information/components/InfoCard.vue

@@ -6,46 +6,59 @@
  */
 
 import { ref } from 'vue';
-import { ElIcon, ElMessage } from 'element-plus';
-import { Delete, EditPen, Picture as IconPicture, Search } from '@element-plus/icons-vue';
+import { ElCol, ElIcon } from 'element-plus';
+import { EditPen, Picture as IconPicture, Plus, Search } from '@element-plus/icons-vue';
 import * as api from '/@/views/computer-information/api';
 import { useRouter } from 'vue-router';
 import { usePagination } from '/@/utils/usePagination';
 import { useTableData } from '/@/utils/useTableData';
+import EditComputerInfo from '/@/views/computer-information/components/EditComputerInfo.vue';
+import { useResponse } from '/@/utils/useResponse';
+import CreateComputer from '/@/views/computer-information/components/CreateComputer.vue';
 
 const router = useRouter();
 const loading = ref();
+
+const showDialog = ref(false);
+const isDrawerVisible = ref(false);
+
+const computerInfo = ref([]);
+const shopOptions = ref([]);
+const userOptions = ref([]);
+
 const { tableOptions, handlePageChange } = usePagination(fetchCardData);
+tableOptions.value.limit = 12;
 
 async function fetchCardData() {
 	const query = {
 		page: tableOptions.value.page,
 		limit: tableOptions.value.limit,
 	};
-	await useTableData(api.getCardData, query, tableOptions);
+	await useTableData(api.getCardData, query, tableOptions,loading);
+}
+
+// 打开创建弹窗
+async function addComputer() {
+  showDialog.value = true;
 }
 
 const checkItem = (item) => {
 	router.push({
 		path: '/computer/detail',
-		query: { id: item.id },
-	});
-};
-
-// 编辑和删除的事件处理
-const editItem = (item) => {
-	router.push({
-		path: '/computer/edit',
-		query: { computerNumber: item.computerNumber },
+		query: {
+			id: item.id,
+			computerNumber: item.computerNumber,
+		},
 	});
 };
 
-const deleteItem = (item) => {
-	ElMessage({
-		message: `删除 ${item.computerNumber}`,
-		type: 'warning',
-	});
-};
+async function editItem(item) {
+	const res = await useResponse(item.id, api.getComputerDetailOverview);
+	computerInfo.value = res.data;
+	if (computerInfo.value) {
+		isDrawerVisible.value = true; // 显示 Drawer
+	}
+}
 
 // 处理图片地址
 const getImageUrl = (images) => {
@@ -53,61 +66,100 @@ const getImageUrl = (images) => {
 	return images.length > 0 ? images[0].image_url : '';
 };
 
+async function fetchShopOptions() {
+  try {
+    const resp = await useResponse(null, api.getShopList);
+    shopOptions.value = resp.data.map((item: any) => {
+      return { value: item.id, label: item.platformNumber };
+    });
+  } catch (e) {
+    console.log('error', e);
+  }
+}
+
+async function fetchUserOptions() {
+  try {
+    const resp = await useResponse(null, api.getUserList);
+    userOptions.value = resp.data.map((item: any) => {
+      return { value: item.id, label: item.name };
+    });
+  } catch (e) {
+    console.log('error', e);
+  }
+}
+
 onMounted(() => {
 	fetchCardData();
+  fetchShopOptions();
+  fetchUserOptions();
 });
 </script>
 
 <template>
+  <!-- 标题区域 -->
+  <el-card class="mb-5"  style="border: none;">
+    <div class="flex justify-between items-baseline">
+      <div>
+        <span class="font-bold text-xl">电脑信息概览</span>
+        <el-divider class=" text-3xl" direction="vertical"/>
+      </div>
+      <span>
+           <el-button :icon="Plus" text bg type="primary" @click="addComputer">添 加</el-button>
+        </span>
+    </div>
+  </el-card>
 	<!-- 卡片展示区域 -->
-	<el-card shadow="never" style="border: none; min-height: 850px; position: relative;">
-		<div class="card-container">
-			<el-row :gutter="20">
-				<el-col v-for="(item, index) in tableOptions.data" :key="index" :span="4" class="my-2.5">
-					<el-card class="item-card" shadow="hover">
-						<el-image :src="getImageUrl(item.images)" alt="电脑图片" class="card-image">
-							<template #error>
-								<el-icon class="card-image" style="font-size: 4rem">
-									<icon-picture />
-								</el-icon>
-							</template>
-						</el-image>
-						<div class="card-content">
-							<div>
-								<span style="color: #808d97">电脑编号: </span>
-								<span>{{ item.computerNumber }}</span>
-							</div>
-							<div>
-								<span style="color: #808d97">所属店铺: </span>
-								<span>{{ item.shopName }}</span>
-							</div>
-						</div>
-						<div class="card-footer">
-							<el-button :icon="Search" circle type="primary" @click="checkItem(item)" />
-							<el-button :icon="EditPen" circle type="warning" @click="editItem(item)" />
-							<el-button :icon="Delete" circle type="danger" @click="deleteItem(item)" />
-						</div>
-					</el-card>
-				</el-col>
-			</el-row>
-		</div>
-		<div class="pagination-container" style="position: absolute; right: 32px; bottom: 32px;">
-			<el-pagination
-				v-model:current-page="tableOptions.page"
-				v-model:page-size="tableOptions.limit"
-				:page-sizes="[5, 10, 25, 50, 100, 200]"
-				:total="tableOptions.total"
-				background
-				layout="sizes, prev, pager, next, total"
-				@change="handlePageChange"
-			/>
-		</div>
-	</el-card>
+  <el-card class="mb-5" style="border: none;min-height: 83vh;">
+    <el-card class="card-container" v-loading="loading" shadow="never" style="border: none;">
+        <el-row :gutter="20">
+          <el-col v-for="(item, index) in tableOptions.data" :key="index" :lg="6" :md="8" :sm="8" :xl="4" :xs="12" class="my-2.5">
+            <el-card class="item-card" shadow="hover">
+              <el-image :src="getImageUrl(item.images)" alt="电脑图片" class="card-image">
+                <template #error>
+                  <el-icon class="card-image" style="font-size: 4rem">
+                    <icon-picture />
+                  </el-icon>
+                </template>
+              </el-image>
+              <div class="card-content">
+                <div>
+                  <span style="color: #808d97; font-weight: 500">电脑编号: </span>
+                  <span style="font-weight: 500">{{ item.computerNumber }}</span>
+                </div>
+                <div>
+                  <span style="color: #808d97; font-weight: 500">所属店铺: </span>
+                  <span style="font-weight: 500">{{ item.shopName }}</span>
+                </div>
+              </div>
+              <div class="card-footer">
+                <el-button :icon="Search" bg circle text type="primary" @click="checkItem(item)" />
+                <el-button :icon="EditPen" bg circle text type="warning" @click="editItem(item)" />
+              </div>
+            </el-card>
+          </el-col>
+        </el-row>
+    </el-card>
+    <div class="pagination-container" style="position: absolute; right: 40px; bottom: 50px">
+      <el-pagination
+          v-model:current-page="tableOptions.page"
+          v-model:page-size="tableOptions.limit"
+          :page-sizes="[6, 12, 24, 36, 48, 60]"
+          :total="tableOptions.total"
+          background
+          layout="sizes, prev, pager, next, total"
+          @change="handlePageChange"
+      />
+    </div>
+  </el-card>
+  <!-- 新增 Dialog -->
+  <CreateComputer v-model="showDialog" :shopOptions :userOptions @refresh="fetchCardData"></CreateComputer>
+  <!-- 编辑 Drawer -->
+  <EditComputerInfo v-if="isDrawerVisible" @refresh="fetchCardData" v-model="isDrawerVisible" :shopOptions="shopOptions" :userOptions="userOptions" :computerInfo :computerNumber="computerInfo.computerNumber" />
 </template>
 
 <style lang="scss" scoped>
 .card-container {
-	margin-bottom: 20px;
+	margin-bottom: 30px;
 }
 
 .item-card {
@@ -130,7 +182,7 @@ onMounted(() => {
 .card-footer {
 	display: flex;
 	justify-content: flex-end;
-	padding: 10px;
+	// padding: 10px;
 }
 
 .pagination-container {

+ 6 - 2
src/views/computer-information/index.vue

@@ -6,11 +6,15 @@
  */
 
 import InfoCard from '/@/views/computer-information/components/InfoCard.vue';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '/@/views/computer-information/api';
+import { ref } from 'vue';
+
 </script>
 
 <template>
-	<div class="px-5 py-5">
-			<InfoCard />
+	<div class="flex flex-col p-5">
+		<InfoCard />
 	</div>
 </template>
 

+ 13 - 2
src/views/computer-information/useColumns.tsx

@@ -1,8 +1,19 @@
-export const ComputerColumns = [
+export const ComputerCurrentColumns = [
   { field: 'platformNumber', title: '平台编号'},
   { field: 'platform', title: '平台'},
   { field: 'platformName', title: '平台名称'},
   { field: 'country', title: '区域'},
   { field: 'userName', title: '使用人'},
   { field: 'company', title: '所属公司'},
-];
+];
+
+export const ComputerPastColumns =[
+  { field: 'create_datetime', title: '时间'},
+  { field: 'station', title: '工位号'},
+  { field: 'computerNumber', title: '平台编号'},
+  { field: 'platform', title: '平台'},
+  { field: 'platformName', title: '平台名称'},
+  { field: 'company', title: '区域'},
+  { field: 'userName', title: '使用人'},
+  { field: 'ipaddress', title: 'IP地址'},
+]

+ 61 - 0
src/views/employee-information/api.ts

@@ -0,0 +1,61 @@
+import { request } from '/@/utils/service';
+
+const apiPrefix = '/api/assets/people/';
+
+// 卡片展示接口
+export function getCardData(query: any) {
+	return request({
+		url: apiPrefix + 'card/',
+		method: 'GET',
+		params: query,
+	});
+}
+
+// 单条查询接口
+export function getEmployeeDetailOverview(id: number) {
+	return request({
+		url: `${apiPrefix}${id}/`,
+		method: 'GET',
+	});
+}
+
+export function getComputerTableData(query: any) {
+	return request({
+		url: apiPrefix + 'computer/',
+		method: 'GET',
+		params: query,
+	});
+}
+
+export function getShopTableData(query: any) {
+	return request({
+		url: apiPrefix + 'shop/',
+		method: 'GET',
+		params: query,
+	});
+}
+
+// 创建接口
+export function createEmployee(body: any) {
+	return request({
+		url: apiPrefix + 'create/',
+		method: 'POST',
+		data: body,
+		headers: {
+			'Content-Type': 'multipart/form-data',
+		}
+	});
+}
+
+// 更新接口
+export function updateEmployeeDetail(query: any) {
+	return request({
+		url: apiPrefix + `${query.id}/`,
+		method: 'POST',
+		params: { partial: query.partial },
+		data: query.dataToUpload,
+		headers: {
+			'Content-Type': 'multipart/form-data',
+		}
+	});
+}

+ 144 - 0
src/views/employee-information/components/CreateEmployee.vue

@@ -0,0 +1,144 @@
+<script lang="ts" setup>
+/**
+ * @Name: CreateEmployee.vue
+ * @Description:
+ * @Author: xinyan
+ */
+import { ElMessage, FormInstance, FormRules } from 'element-plus';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '../api';
+
+const showDialog = defineModel({ default: false });
+const emit = defineEmits([ 'refresh' ]);
+
+// 图片处理
+const upload = ref<UploadInstance>();
+// 表单数据
+interface RuleForm {
+  name: string;
+  department: string;
+  phone: string;
+  email: string;
+  user: string;
+  macaddress: string;
+  ipaddress: string;
+  images: UploadFile[];
+}
+const ruleFormRef = ref<FormInstance>();
+const formData = reactive<RuleForm>({
+  name: '',
+  department: '',
+  phone: '',
+  email: '',
+  images: [],
+});
+
+const rules = reactive<FormRules<RuleForm>>({
+  name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
+  department: [{ required: true, message: '请输入部门', trigger: 'blur' }],
+  phone: [{ required: true, message: '请输入电话号码', trigger: 'blur' },
+    { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号码', trigger: 'blur' }],
+  email: [{ required: true, message: '请输入邮箱', trigger: 'blur' },
+  { type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' }],
+});
+
+
+// 超过图片限制时触发的回调
+const handleExceed: UploadProps['onExceed'] = (files) => {
+  upload.value!.clearFiles();
+  const file = files[0] as UploadRawFile;
+  file.uid = genFileId();
+  upload.value!.handleStart(file);
+};
+
+const submitForm = async (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      handleSave();
+      showDialog.value = false;
+      emit('refresh');
+    } else {
+      ElMessage.error('添加失败');
+    }
+  })
+}
+// 保存操作
+async function handleSave() {
+  const bodyData = new FormData();
+  const fieldsToAppend = ['name', 'department', 'phone', 'email'];
+  fieldsToAppend.forEach((field) => {
+    bodyData.append(field, formData[field as keyof RuleForm]);
+  });
+
+  if (formData.images.length > 0 && formData.images[0].raw) {
+    bodyData.append('images', formData.images[0].raw); // 使用 'images' 作为字段名,将 File 对象添加到 FormData 中
+  }
+  await useResponse(bodyData, api.createEmployee);
+  if (resp.code === 2000) {
+    ElMessage.success('添加成功');
+  }
+}
+
+// 取消操作
+const handleCancel = () => {
+  showDialog.value = false;
+};
+
+onMounted(() => {
+})
+</script>
+
+<template>
+  <el-dialog v-model="showDialog" :close-on-click-modal="false" title="新增电脑信息" width="35%">
+    <el-form
+        ref="ruleFormRef"
+        :model="formData"
+        :rules="rules"
+        class="employee-info-form"
+        label-width="auto"
+        status-icon>
+      <el-form-item label="姓名:" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入姓名" />
+      </el-form-item>
+      <el-form-item label="部门:" prop="department">
+        <el-input v-model="formData.department" placeholder="请输入部门" />
+      </el-form-item>
+      <el-form-item label="电话号码:" prop="phone">
+        <el-input v-model="formData.phone" placeholder="请输入电话号码" />
+      </el-form-item>
+      <el-form-item label="邮箱:" prop="email">
+        <el-input v-model="formData.email" placeholder="请输入邮箱" />
+      </el-form-item>
+      <!-- 人员图片上传 -->
+      <el-form-item :key="images" label="人员图片:">
+        <el-upload
+            ref="upload"
+            v-model:file-list="formData.images"
+            :auto-upload="false"
+            :limit="1"
+            :on-exceed="handleExceed"
+            action="#"
+            list-type="picture-card"
+        >
+          <el-icon>
+            <Plus />
+          </el-icon>
+        </el-upload>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button>
+        <el-button @click="handleCancel">取消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<style scoped>
+.dialog-footer {
+  margin-bottom: 10px;
+  text-align: center;
+}
+</style>

+ 183 - 0
src/views/employee-information/components/EditEmployeeInfo.vue

@@ -0,0 +1,183 @@
+<script lang="ts" setup>
+/**
+ * @Name: EditEmployeeInfo.vue
+ * @Description: 编辑电脑信息
+ * @Author: xinyan
+ */
+import { ref } from 'vue';
+import { ElIcon, ElMessage, FormInstance, FormRules, genFileId, UploadInstance, UploadProps, UploadRawFile } from 'element-plus';
+import { Plus } from '@element-plus/icons-vue';
+import { useResponse } from '/@/utils/useResponse';
+import * as api from '../api';
+
+const visible = defineModel({ default: false });
+const { employeeInfo, name } = defineProps<{
+	employeeInfo: any;
+	name: any;
+}>();
+
+const emit = defineEmits(['refresh']);
+
+// 图片处理
+const upload = ref<UploadInstance>();
+
+// 表单数据
+interface RuleForm {
+	name: string;
+	department: string;
+	phone: string;
+	email: string;
+	user: string;
+	macaddress: string;
+	ipaddress: string;
+	images: UploadFile[];
+}
+
+const ruleFormRef = ref<FormInstance>();
+const formData = reactive<RuleForm>({
+	name: '',
+	department: '',
+	phone: '',
+	email: '',
+	images: [],
+});
+
+const rules = reactive<FormRules<RuleForm>>({
+	name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
+	department: [{ required: true, message: '请输入部门', trigger: 'blur' }],
+	phone: [{ required: true, message: '请输入电话号码', trigger: 'blur' }],
+	email: [{ required: true, message: '请输入所属商铺', trigger: 'blur' }],
+});
+
+onBeforeMount(() => {
+	replaceCol();
+});
+
+// 超过图片限制时触发的回调
+const handleExceed: UploadProps['onExceed'] = (files) => {
+	upload.value!.clearFiles();
+	const file = files[0] as UploadRawFile;
+	file.uid = genFileId();
+	upload.value!.handleStart(file);
+};
+
+const submitForm = async (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+
+	await formEl.validate(async (valid, fields) => {
+		if (valid) {
+			const dataToUpload = new FormData();
+
+			for (const key in formData) {
+				if (key !== 'images' && formData.hasOwnProperty(key)) {
+					dataToUpload.append(key, formData[key]);
+				}
+			}
+
+			if (Array.isArray(formData.images)) {
+				formData.images.forEach((image: any) => {
+					if (image.raw) {
+						dataToUpload.append('images', image.raw);
+					}
+				});
+			}
+			try {
+				const resp = await useResponse({ id: employeeInfo.id, partial: 1, dataToUpload, }, api.updateEmployeeDetail);
+				// 检查上传结果并提示用户
+				if (resp.code === 2000) {
+					ElMessage.success('修改成功');
+					visible.value = false;
+					emit('refresh');
+				}
+			} catch (error) {
+				console.error('上传失败:', error);
+				ElMessage.error('上传失败,请重试');
+			}
+		} else {
+			console.log('error submit!', fields);
+		}
+	});
+};
+
+const resetForm = (formEl: FormInstance | undefined) => {
+	if (!formEl) return;
+	formEl.resetFields();
+};
+
+function replaceCol() {
+	const result = Object.keys(formData).reduce((acc, key) => {
+		if (key in employeeInfo) {
+			// 如果字段是 images,则转换图片格式
+			if (key === 'images' && Array.isArray(employeeInfo[key])) {
+				acc[key] = employeeInfo[key].map((img: { image: string }) => ({
+					name: '已上传图片',
+					url: img.image_url,
+				}));
+			} else {
+				acc[key] = employeeInfo[key];
+			}
+		}
+		return acc;
+	}, {} as { [key: string]: any });
+	Object.assign(formData, result);
+}
+
+onMounted(() => {});
+</script>
+
+<template>
+	<el-drawer v-model="visible" :title="`人员编辑 - ${name}`" size="30%">
+		<el-form ref="ruleFormRef" :model="formData" :rules="rules" class="employee-info-form" label-width="auto">
+			<el-form-item label="姓名:" prop="name">
+				<el-input v-model="formData.name" placeholder="请输入姓名" />
+			</el-form-item>
+			<el-form-item label="部门:" prop="department">
+				<el-input v-model="formData.department" placeholder="请输入部门" />
+			</el-form-item>
+			<el-form-item label="电话号码:" prop="phone">
+				<el-input v-model="formData.phone" placeholder="请输入电话号码" />
+			</el-form-item>
+			<el-form-item label="邮箱:" prop="email">
+				<el-input v-model="formData.email" placeholder="请输入邮箱" />
+			</el-form-item>
+			<!-- 电脑图片上传 -->
+			<el-form-item :key="images" label="电脑图片:">
+				<el-upload
+					ref="upload"
+					v-model:file-list="formData.images"
+					:auto-upload="false"
+					:limit="1"
+					:on-exceed="handleExceed"
+					action="#"
+					list-type="picture-card"
+				>
+					<el-icon>
+						<Plus />
+					</el-icon>
+				</el-upload>
+			</el-form-item>
+
+			<!-- 按钮组 -->
+			<el-form-item class="button-group">
+				<el-button type="primary" @click="submitForm(ruleFormRef)">保存</el-button>
+				<el-button @click="resetForm(ruleFormRef)">取消</el-button>
+			</el-form-item>
+		</el-form>
+	</el-drawer>
+</template>
+
+<style scoped>
+.employee-info-form {
+	padding: 20px;
+}
+
+.button-group {
+	margin-top: 20px;
+	text-align: center;
+}
+
+.employee-info-form :deep(.el-form-item__label) {
+	font-size: 14px;
+	font-weight: 500;
+}
+</style>

+ 193 - 0
src/views/employee-information/components/EmployeeDetail.vue

@@ -0,0 +1,193 @@
+<script lang="ts" setup>
+/**
+ * @Name: EmployeeDetail.vue
+ * @Description: 电脑信息-当前
+ * @Author: xinyan
+ */
+import { useResponse } from '/@/utils/useResponse';
+import { EmployeeShopColumns, EmployeeComputerColumns } from '/@/views/employee-information/useColumns';
+import * as api from '../api';
+import { Picture as IconPicture } from '@element-plus/icons-vue';
+import { useTableData } from '/@/utils/useTableData';
+import { usePagination } from '/@/utils/usePagination';
+
+const route = useRoute();
+const id = route.query.id;
+const employeeOverview: any = ref([]);
+const overviewLoading = ref();
+const currentView = ref('shop');
+const { tableOptions, handlePageChange } = usePagination(fetchEmployeeData);
+
+const gridOptions: any = reactive({
+  border: 'inner',
+  round: true,
+  stripe: true,
+  shopRowHighLight: true,
+  height: 700,
+  toolbarConfig: {
+    custom: true,
+    slots: {
+      buttons: 'toolbar_buttons',
+      // tools: 'toolbar_tools'
+    },
+  },
+  rowConfig: {
+    isHover: true,
+  },
+  columnConfig: {
+    resizable: true,
+  },
+  // pagerConfig: {
+  //   total: tableOptions.value.total,
+  //   page: tableOptions.value.page,
+  //   limit: tableOptions.value.limit,
+  // },
+  loading: false,
+  loadingConfig: {
+    icon: 'vxe-icon-indicator roll',
+    text: '正在拼命加载中...',
+  },
+  columns: EmployeeShopColumns,
+  data: [],
+});
+
+// function pageChange({ pageSize, shopPage }: any) {
+//   gridOptions.pagerConfig.limit = pageSize;
+//   gridOptions.pagerConfig.page = shopPage;
+//   fetchEmployeeData();
+// }
+
+// 当前信息、历史记录
+async function fetchEmployeeData(view) { // 默认为当前视图
+  const query = {
+    // page: gridOptions.pagerConfig.page,
+    // limit: gridOptions.pagerConfig.limit,
+  };
+
+  switch (view) {
+    case 'shop':
+      gridOptions.columns = EmployeeShopColumns;
+      currentView.value = 'shop';
+      query.id = id;
+      await useTableData(api.getShopTableData, query, gridOptions);
+      break;
+    case 'computer':
+      gridOptions.columns = EmployeeComputerColumns;
+      currentView.value = 'computer';
+      query.id = id;
+      await useTableData(api.getComputerTableData, query, gridOptions);
+      break;
+  }
+}
+
+function switchView(view) {
+  if (view !== currentView.value) { // 只有在不同的视图时才切换
+    fetchEmployeeData(view); // 调用 fetchEmployeeData 来加载新的视图数据
+  }
+}
+
+async function fetchEmployeeDetailOverview() {
+  const res = await useResponse(id, api.getEmployeeDetailOverview, overviewLoading);
+  employeeOverview.value = res.data;
+}
+
+const getImageSrc = () => {
+  // 如果 `images` 有值,则返回第一张图片的 URL;否则返回占位图
+  return employeeOverview.value.images && employeeOverview.value.images.length > 0
+      ? employeeOverview.value.images[0].image_url
+      : 'https://via.placeholder.com/150';
+};
+
+// 表格样式
+const cellStyle = () => {
+  return {
+    fontSize: '12px',
+    fontWeight: '600',
+  };
+};
+
+const headerCellStyle = () => {
+  return {
+    fontSize: '12px',
+  };
+};
+
+onMounted(() => {
+  fetchEmployeeData(currentView.value);
+  fetchEmployeeDetailOverview();
+});
+</script>
+
+<template>
+  <div class="p-2.5">
+    <!-- overview-card -->
+    <el-card v-loading="overviewLoading" body-class="flex items-center" shadow="hover" style="border: none">
+      <el-image :src="getImageSrc()" class="mr-7 rounded-2xl" style="height: 100px; width: 100px; object-fit: contain">
+        <template #error>
+          <div class="mr-3.5 flex items-center justify-center text-5xl" style="height: 100px; width: 100px; background-color: #f5f5f5">
+            <el-icon>
+              <icon-picture />
+            </el-icon>
+          </div>
+        </template>
+      </el-image>
+      <el-col :span="18">
+        <div class="info-container text-lg">
+          <div class="info-column">
+            <div class="font-semibold">
+              姓名:
+              <span class="font-medium italic ml-1.5" style="color: #64748b">{{ employeeOverview.name }}</span>
+            </div>
+            <div class="font-semibold">
+              所属部门:
+              <span class="font-medium italic ml-1.5" style="color: #64748b">{{ employeeOverview.department }}</span>
+            </div>
+            <div class="font-semibold">
+              电话:
+              <span class="font-medium italic ml-1.5" style="color: #64748b">{{ employeeOverview.phone }}</span>
+            </div>
+            <div class="font-semibold">
+              邮箱:
+              <span class="font-medium italic ml-1.5" style="color: #64748b">{{ employeeOverview.email }}</span>
+            </div>
+          </div>
+        </div>
+      </el-col>
+    </el-card>
+    <!-- table-card -->
+    <el-card body-style="padding-top: 10px" class="mt-2.5" shadow="hover" style="border: none">
+      <vxe-grid :cell-style="cellStyle" :header-cell-style="headerCellStyle" v-bind="gridOptions">
+        <template #toolbar_buttons>
+          <el-button :type="currentView === 'shop' ? 'primary' : 'default'" @click="switchView('shop')"> 店铺信息 </el-button>
+          <el-button :type="currentView === 'computer' ? 'primary' : 'default'" @click="switchView('computer')"> 电脑信息 </el-button>
+        </template>
+        <!--<template #pager>-->
+        <!--  <vxe-pager-->
+        <!--      v-model:currentPage="gridOptions.pagerConfig.page"-->
+        <!--      v-model:pageSize="gridOptions.pagerConfig.limit"-->
+        <!--      :total="gridOptions.pagerConfig.total"-->
+        <!--      size="small"-->
+        <!--      @page-change="handlePageChange"-->
+        <!--  >-->
+        <!--  </vxe-pager>-->
+        <!--</template>-->
+      </vxe-grid>
+    </el-card>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.info-container {
+  display: flex;
+  justify-content: space-between;
+}
+
+.info-column {
+  flex: 1;
+  padding: 0 10px;
+}
+
+p {
+  margin: 5px 0;
+}
+</style>

+ 163 - 0
src/views/employee-information/components/InfoCard.vue

@@ -0,0 +1,163 @@
+<script lang="ts" setup>
+/**
+ * @Name: InfoCard.vue
+ * @Description:
+ * @Author: xinyan
+ */
+import { useRouter } from 'vue-router';
+import { ref } from 'vue';
+import { usePagination } from '/@/utils/usePagination';
+import { useTableData } from '/@/utils/useTableData';
+import { Delete, EditPen, Picture as IconPicture, Plus, Search } from '@element-plus/icons-vue';
+import * as api from '/@/views/employee-information/api';
+import { useResponse } from '/@/utils/useResponse';
+import EditEmployeeInfo from '/@/views/employee-information/components/EditEmployeeInfo.vue';
+import CreateEmployee from '/@/views/employee-information/components/CreateEmployee.vue';
+import { ElCol } from 'element-plus';
+
+const router = useRouter();
+const loading = ref();
+// 创建弹窗
+const showDialog = ref(false);
+const isDrawerVisible = ref(false);
+
+const employeeInfo = ref([]);
+const { tableOptions, handlePageChange } = usePagination(fetchCardData);
+tableOptions.value.limit = 12;
+
+async function fetchCardData() {
+	const query = {
+		page: tableOptions.value.page,
+		limit: tableOptions.value.limit,
+	};
+	await useTableData(api.getCardData, query, tableOptions);
+}
+
+async function addEmployee() {
+	showDialog.value = true;
+}
+
+const checkItem = (item) => {
+	router.push({
+		path: '/employee/detail',
+		query: {
+			id: item.id,
+		},
+	});
+};
+
+async function editItem(item) {
+	const res = await useResponse(item.id, api.getEmployeeDetailOverview);
+	employeeInfo.value = res.data;
+	if (employeeInfo.value) {
+		isDrawerVisible.value = true; // 显示 Drawer
+	}
+}
+
+const getImageUrl = (images) => {
+	// 如果有图片,返回第一个图片的 image_url,否则返回占位图
+	return images.length > 0 ? images[0].image_url : '';
+};
+
+onMounted(() => {
+	fetchCardData();
+});
+</script>
+
+<template>
+	<!-- 标题区域 -->
+	<el-card class="mb-5" style="border: none">
+		<div class="flex justify-between items-baseline">
+			<div>
+				<span class="font-bold text-xl">人员信息概览</span>
+				<el-divider class="text-3xl" direction="vertical" />
+			</div>
+			<span>
+				<el-button :icon="Plus" bg text type="primary" @click="addEmployee">添 加</el-button>
+			</span>
+		</div>
+	</el-card>
+	<!-- 卡片展示区域 -->
+	<el-card class="mb-5" style="border: none; min-height: 83vh">
+		<el-card v-loading="loading" class="card-container" shadow="never" style="border: none">
+			<el-row :gutter="20">
+				<el-col v-for="(item, index) in tableOptions.data" :key="index" :lg="6" :md="8" :sm="8" :xl="4" :xs="12" class="my-2.5">
+					<el-card class="item-card" shadow="hover">
+						<el-image :src="getImageUrl(item.images)" alt="电脑图片" class="card-image">
+							<template #error>
+								<el-icon class="card-image" style="font-size: 4rem">
+									<icon-picture />
+								</el-icon>
+							</template>
+						</el-image>
+						<div class="card-content">
+							<div>
+								<span style="color: #808d97; font-weight: 500">姓名: </span>
+								<span style="font-weight: 500">{{ item.name }}</span>
+							</div>
+							<div>
+								<span style="color: #808d97; font-weight: 500">部门: </span>
+								<span style="font-weight: 500">{{ item.department }}</span>
+							</div>
+						</div>
+						<div class="card-footer">
+							<el-button :icon="Search" bg circle text type="primary" @click="checkItem(item)" />
+							<el-button :icon="EditPen" bg circle text type="warning" @click="editItem(item)" />
+							<!--<el-button :icon="Delete" bg circle text type="danger" @click="deleteItem(item)" />-->
+						</div>
+					</el-card>
+				</el-col>
+			</el-row>
+		</el-card>
+    <div class="pagination-container" style="position: absolute; right: 40px; bottom: 50px">
+      <el-pagination
+          v-model:current-page="tableOptions.page"
+          v-model:page-size="tableOptions.limit"
+          :page-sizes="[6, 12, 24, 36, 48, 60]"
+          :total="tableOptions.total"
+          background
+          layout="sizes, prev, pager, next, total"
+          @change="handlePageChange"
+      />
+    </div>
+	</el-card>
+	<!-- 新增 Dialog -->
+	<CreateEmployee v-model="showDialog" @refresh="fetchCardData" />
+	<!-- 编辑 Drawer -->
+	<EditEmployeeInfo v-if="isDrawerVisible" @refresh="fetchCardData" v-model="isDrawerVisible" :employeeInfo :name="employeeInfo.name" />
+</template>
+
+<style lang="scss" scoped>
+.card-container {
+	margin-bottom: 30px;
+}
+
+.item-card {
+	border-radius: 10px;
+	overflow: hidden;
+	position: relative;
+}
+
+.card-image {
+	width: 100%;
+	height: 150px;
+	object-fit: cover;
+}
+
+.card-content {
+	padding: 10px;
+	font-size: 14px;
+}
+
+.card-footer {
+	display: flex;
+	justify-content: flex-end;
+	// padding: 10px;
+}
+
+.pagination-container {
+	display: flex;
+	justify-content: flex-end;
+	// margin-bottom: 20px;
+}
+</style>

+ 10 - 5
src/views/employee-information/index.vue

@@ -1,16 +1,21 @@
-<script setup lang="ts">
+<script lang="ts" setup>
 /**
  * @Name: index.vue
  * @Description: 员工信息页面
  * @Author: Cheney
  */
 
+import InfoCard from '/@/views/employee-information/components/InfoCard.vue';
+import { Plus } from '@element-plus/icons-vue';
+import CreateEmployee from '/@/views/employee-information/components/CreateEmployee.vue';
+
+
 </script>
 
 <template>
-
+  <div class="flex flex-col p-5">
+		<InfoCard />
+	</div>
 </template>
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 17 - 0
src/views/employee-information/useColumns.tsx

@@ -0,0 +1,17 @@
+export const EmployeeShopColumns = [
+  { field: 'platformNumber', title: '平台编号'},
+  { field: 'country', title: '店铺区域'},
+  { field: 'platform', title: '平台'},
+  { field: 'brandName', title: '品牌'},
+  { field: 'company', title: '所属公司'},
+  { field: 'line', title: '线路'},
+  { field: 'ipaddress', title: 'IP地址'},
+];
+
+export const EmployeeComputerColumns =[
+  { field: 'computerNumber', title: '电脑编号'},
+  { field: 'station', title: '工位号'},
+  { field: 'computerType', title: '电脑类型'},
+  { field: 'ipaddress', title: 'IP地址'},
+  { field: 'macaddress', title: 'MAC地址'},
+]