فهرست منبع

feat: ✨ 完成产品列表、完成物联网卡号

@sun-chaoqun 2 سال پیش
والد
کامیت
2ff9581754

+ 12 - 0
src/api/storehouse/index.ts

@@ -41,3 +41,15 @@ export const Storehouse_Product_Add = (params: any) => $http.post('/testapi/stor
 export const Storehouse_Product_Edit = (params: any) => $http.post('/testapi/storage/Product/Edit', params)
 // 产品删除
 export const Storehouse_Product_Del = (params: any) => $http.post('/testapi/storage/Product/Del', params)
+
+/**
+ * 物联网卡
+ */
+// 物联网卡列表
+export const Storehouse_IotCard_List = (params: any) => $http.post('/testapi/storage/IotCard/List', params)
+// 物联网卡添加
+export const Storehouse_IotCard_Add = (params: any) => $http.post('/testapi/storage/IotCard/Add', params)
+// 物联网卡编辑
+export const Storehouse_IotCard_Edit = (params: any) => $http.post('/testapi/storage/IotCard/Edit', params)
+// 物联网卡删除
+export const Storehouse_IotCard_Del = (params: any) => $http.post('/testapi/storage/IotCard/Del', params)

+ 78 - 32
src/components/Upload/index.vue

@@ -1,42 +1,33 @@
 <script setup lang="ts">
-import { ref } from 'vue'
+import { ref, watch } from 'vue'
 import { Plus } from '@element-plus/icons-vue'
 import { UpFileToken } from '@/api/public/index'
-import { ElMessage, genFileId } from 'element-plus'
+import { ElMessage, genFileId, ElLoading } from 'element-plus'
 import { GlobalStore } from '@/stores/index'
-import type { UploadProps, UploadRawFile } from 'element-plus'
-
-const upload = ref()
-const globalStore = GlobalStore()
+import type { UploadProps, UploadRawFile, UploadUserFile, UploadFile, UploadFiles } from 'element-plus'
 
 interface FileListType {
   url?: string
-  name?: string
+  name?: string | number
 }
 
 interface ProTableProps {
   isImg?: boolean // 是否只能上传图片
   disabled?: boolean
-  fileList?: FileListType[]
-  // beforeUpload: () => void
-  // onSuccess: () => Promise<any>
-  // onError: () => Promise<any>
-  // handleExceed: () => void
-  // handleRemove: () => void
-  // handlePictureCardPreview: () => void
-  // uploadData: {
-  //   token: string
-  //   key: string
-  // }
+  limit: number
+  modelValue: string | string[]
 }
 
-// let prove_img = true
+const upload = ref()
+const globalStore = GlobalStore()
 const dialogImageUrl = ref('')
 const dialogVisible = ref(false)
 const uploadData = {
   token: '',
   key: ''
 }
+let loadingInstance: any = null
+const emit = defineEmits<{ (event: 'update:modelValue', value: string | string[]): void }>()
 
 // 图片查看 放大
 const handlePictureCardPreview = async (file: any) => {
@@ -50,14 +41,20 @@ const beforeUpload = async (file: any) => {
     ElMessage.error('必须上传图片!!')
     return
   }
-  // prove_img = false
+  loadingInstance = ElLoading.service({
+    lock: true,
+    text: 'Loading',
+    background: 'rgba(0, 0, 0, 0.7)'
+  }) // 加载蒙版
   let suffix = file.type.split('/')[1]
   const res: any = await UpFileToken({ User_tokey: globalStore.GET_User_tokey, T_suffix: suffix })
   uploadData.token = res.Data
   uploadData.key = file.name
 }
 // 图片上传超出界限
-const handleExceed: UploadProps['onExceed'] = (files: any) => {
+const handleExceed: UploadProps['onExceed'] = (files: any, uploadFiles: UploadUserFile[]) => {
+  console.log(files, uploadFiles)
+
   upload.value.clearFiles()
   const file = files[0] as UploadRawFile
   file.uid = genFileId()
@@ -65,39 +62,83 @@ const handleExceed: UploadProps['onExceed'] = (files: any) => {
   upload.value.submit()
 }
 // 图片上传成功
-const onSuccess = (response: any) => {
-  // form.value.T_prove_img = response.key
-  // prove_img = true
+const onSuccess = (response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) => {
+  let ERPOSS = import.meta.env.VITE_BZD_ERPOSS_APP_API
+  if (limit.value === 1) {
+    emit('update:modelValue', ERPOSS + response.key)
+  } else {
+    let keys = uploadFiles.map((item: any) => ERPOSS + item.response.key)
+    emit('update:modelValue', keys)
+  }
   ElMessage.success('图片上传成功!!')
+  loadingInstance.close()
 }
 // 图片上传失败
 const onError = () => {
   ElMessage.error('图片上传失败!!')
 }
 // 图片移除
-const handleRemove: UploadProps['onRemove'] = () => {
-  // form.value.T_prove_img = ''
+const handleRemove: UploadProps['onRemove'] = (uploadFile: any, uploadFiles: UploadFiles) => {
+  if (limit.value === 1) {
+    emit('update:modelValue', '')
+  } else {
+    let keys = uploadFiles.map((item: any) => {
+      if (item.response.key !== uploadFile.response.key) {
+        return item.response.key
+      }
+    })
+    emit('update:modelValue', keys)
+  }
 }
 
 // 接受父组件参数,配置默认值
 const props = withDefaults(defineProps<ProTableProps>(), {
-  disabled: true,
-  fileList: () => []
+  disabled: false
+  // modelValue: ''
 })
 
-const fileList = ref<FileListType[]>(props.fileList)
+const fileList = ref<FileListType[]>([])
 const isImg = ref<boolean>(props.isImg)
+const limit = ref<number>(props.limit)
+
+watch(
+  () => props.modelValue,
+  pre => {
+    if (pre && fileList.value.length !== limit.value) {
+      if (limit.value === 1) {
+        fileList.value.push({
+          url: pre as string,
+          name: genFileId()
+        })
+      } else {
+        ;(pre as string[]).forEach((item: string) => {
+          fileList.value.push({
+            url: item,
+            name: genFileId()
+          })
+        })
+      }
+    }
+  }
+)
+
+const clearfileList = () => {
+  fileList.value = []
+}
+defineExpose({
+  clearfileList
+})
 </script>
 
 <template>
-  <div class="upload">
+  <div>
     <el-upload
       ref="upload"
       :disabled="disabled"
       v-model:file-list="fileList"
       action="https://up-z2.qiniup.com"
       list-type="picture-card"
-      :limit="1"
+      :limit="limit"
       :auto-upload="true"
       :before-upload="beforeUpload"
       :on-success="onSuccess"
@@ -115,4 +156,9 @@ const isImg = ref<boolean>(props.isImg)
   </div>
 </template>
 
-<style scoped></style>
+<style scoped>
+.full-img {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 8 - 0
src/router/modules/staticRouter.ts

@@ -36,6 +36,14 @@ export const staticRouter: RouteRecordRaw[] = [
         meta: {
           title: '产品列表'
         }
+      },
+      {
+        path: '/ioTNetworkCard',
+        name: 'IoTNetworkCard',
+        component: () => import('@/views/storehouse/IoTNetworkCard.vue'),
+        meta: {
+          title: '物联网卡'
+        }
       }
     ]
   },

+ 1 - 1
src/style.scss

@@ -21,7 +21,7 @@ body,
 }
 body {
   line-height: 1.5em;
-  font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif;
+  font-family: 'Trebuchet MS', Arial, Helvetica, sans-serif !important;
   line-height: 25px;
   letter-spacing: 1px;
   font-size: 14px;

+ 240 - 3
src/views/storehouse/IoTNetworkCard.vue

@@ -1,7 +1,244 @@
-<script setup lang="ts"></script>
+<script setup lang="ts">
+import {
+  Storehouse_IotCard_List,
+  Storehouse_IotCard_Add,
+  Storehouse_IotCard_Edit,
+  Storehouse_IotCard_Del
+} from '@/api/storehouse/index'
+import { GlobalStore } from '@/stores/index'
+import { ref, reactive, nextTick } from 'vue'
+import Drawer from '@/components/Drawer/index.vue'
+import TableBase from '@/components/TableBase/index.vue'
+import type { FormInstance, FormRules } from 'element-plus'
+import { Edit, Delete } from '@element-plus/icons-vue'
+import type { ColumnProps } from '@/components/TableBase/interface/index'
+import { ElMessageBox, ElMessage } from 'element-plus'
+
+const isNew = ref(true)
+const globalStore = GlobalStore()
+const formLabelWidth = ref('120px')
+const ruleFormRef = ref<FormInstance>()
+const drawerRef = ref<InstanceType<typeof Drawer> | null>(null)
+const TableRef = ref<InstanceType<typeof TableBase> | null>(null)
+
+const columns: ColumnProps[] = [
+  { type: 'index', label: '序号', width: 80 },
+  { prop: 'T_iccid', label: '物联网卡号' },
+  { prop: 'T_sn', label: '关联SN' },
+  { prop: 'T_State', label: '状态', name: 'T_State' },
+  { prop: 'T_type', label: '类型' },
+  { prop: 'operation', label: '操作', width: 200, fixed: 'right' }
+]
+
+// 添加仓库名称
+type Fn = () => void
+const form = reactive({
+  T_id: '',
+  T_sn: '',
+  T_type: '',
+  T_state: null,
+  T_iccid: ''
+})
+
+const validate_T_sn = (rule: any, value: any, callback: any) => {
+  if (form.T_state === 2 && value === '') {
+    callback(new Error('请输入关联SN'))
+  } else {
+    callback()
+  }
+}
+
+const rules = reactive<FormRules>({
+  T_iccid: [{ required: true, message: '请输入物联网卡号', trigger: 'blur' }],
+  T_state: [{ required: true, message: '请选择状态', trigger: 'blur' }],
+  T_type: [{ required: true, message: '请输入型号', trigger: 'blur' }],
+  T_sn: [{ validator: validate_T_sn, trigger: 'blur' }]
+})
+
+const callbackDrawer = (done: Fn) => {
+  resetForm(ruleFormRef.value)
+  done()
+}
+const openDrawer = (type: string, row?: any) => {
+  isNew.value = type === 'new' ? true : false
+  nextTick(() => {
+    form.T_id = row.Id
+    form.T_sn = row.T_sn
+    form.T_type = row.T_type
+    form.T_state = row.T_State
+    form.T_iccid = row.T_iccid
+  })
+  drawerRef.value?.openDrawer()
+}
+
+const resetForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.resetFields()
+}
+
+const AddUserName = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.validate(async valid => {
+    if (valid) {
+      let res: any = {}
+      if (isNew.value) {
+        console.log(form)
+        res = await Storehouse_IotCard_Add({ User_tokey: globalStore.GET_User_tokey, ...form })
+      } else {
+        res = await Storehouse_IotCard_Edit({
+          User_tokey: globalStore.GET_User_tokey,
+          ...form
+        })
+      }
+      if (res.Code === 200) {
+        ElMessage.success(`${isNew.value ? '添加' : '修改'}物联网卡成功!!`)
+        nextTick(() => {
+          drawerRef.value?.closeDrawer()
+          TableRef.value?.getTableList()
+          resetForm(ruleFormRef.value)
+          isNew.value = true
+        })
+      }
+    } else {
+      return false
+    }
+  })
+}
+
+// 删除
+const UserDelete = (row: any) => {
+  ElMessageBox.confirm('您确定要删除该物联网卡号吗?', '警告', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  })
+    .then(async () => {
+      const res: any = await Storehouse_IotCard_Del({ User_tokey: globalStore.GET_User_tokey, T_id: row.Id })
+      if (res.Code === 200) {
+        ElMessage.success('删除成功!')
+        nextTick(() => {
+          TableRef.value?.getTableList()
+        })
+      }
+    })
+    .catch(() => {
+      ElMessage.warning('取消成功!')
+    })
+}
+
+// 搜索
+const options = reactive([
+  { name: '未使用', id: 1 },
+  { name: '已使用', id: 2 },
+  { name: '已作废', id: 3 }
+])
+const initParam = reactive({
+  User_tokey: globalStore.GET_User_tokey,
+  T_name: '',
+  T_state: ''
+})
+const searchHandle = () => {
+  TableRef.value?.searchTable()
+}
+</script>
 
 <template>
-  <div class="classify">物联网卡列表</div>
+  <div class="IoTNetworkCard">
+    <TableBase ref="TableRef" :columns="columns" :requestApi="Storehouse_IotCard_List" :initParam="initParam">
+      <template #table-header>
+        <div class="input-suffix">
+          <el-row :gutter="20" style="margin-bottom: 0">
+            <el-col :xl="6" :lg="8" :md="10">
+              <span class="inline-flex items-center">关键词:</span>
+              <el-input
+                v-model="initParam.T_name"
+                type="text"
+                class="w-50 m-2"
+                placeholder="按物联网卡号或SN搜索"
+                @change="searchHandle"
+              />
+            </el-col>
+            <el-col :xl="10" :md="12">
+              <span class="inline-flex items-center">产品分类:</span>
+              <el-select v-model="initParam.T_state" class="w-50 m-2" clearable placeholder="请选择状态~">
+                <el-option v-for="item in options" :key="item.id" :label="item.name" :value="item.id" />
+              </el-select>
+              <el-button type="primary" @click="searchHandle">搜索</el-button>
+            </el-col>
+            <el-col :xl="6" :md="2" class="btn"
+              ><el-button type="primary" @click="openDrawer('new')">添加</el-button></el-col
+            >
+          </el-row>
+        </div>
+      </template>
+      <template #T_State="{ row }">
+        <el-tag v-if="row.T_State === 2" type="success" effect="dark"> 已使用 </el-tag>
+        <el-tag v-else-if="row.T_State === 1" type="warning" effect="dark"> 未使用 </el-tag>
+        <el-tag v-else type="info" effect="dark"> 已作废 </el-tag>
+      </template>
+      <template #right="{ row }">
+        <el-button
+          :disabled="row.T_State === 2"
+          link
+          type="primary"
+          size="small"
+          :icon="Edit"
+          @click="openDrawer('edit', row)"
+          >编辑</el-button
+        >
+        <el-button :disabled="row.T_State === 2" link type="danger" size="small" :icon="Delete" @click="UserDelete(row)"
+          >删除</el-button
+        >
+      </template>
+    </TableBase>
+    <Drawer ref="drawerRef" :handleClose="callbackDrawer">
+      <template #header="{ params }">
+        <h4 :id="params.titleId" :class="params.titleClass">{{ isNew ? '添加' : '编辑' }} - 物联网卡</h4>
+      </template>
+      <el-form ref="ruleFormRef" :model="form" :rules="rules">
+        <el-divider border-style="dashed" />
+        <el-form-item label="物联网卡号:" :label-width="formLabelWidth" prop="T_number">
+          <el-input v-model="form.T_iccid" type="text" autocomplete="off" placeholder="请输入物联网卡号" />
+        </el-form-item>
+        <el-form-item label="型号:" :label-width="formLabelWidth" prop="T_type">
+          <el-input v-model="form.T_type" type="text" autocomplete="off" placeholder="请输入型号" />
+        </el-form-item>
+        <el-form-item label="状态:" :label-width="formLabelWidth" prop="T_state">
+          <el-radio-group v-model="form.T_state">
+            <el-radio :label="2">已使用</el-radio>
+            <el-radio :label="1">未使用</el-radio>
+            <el-radio :label="3">已作废</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="关联SN:" :label-width="formLabelWidth" prop="T_sn">
+          <el-input v-model="form.T_sn" type="text" autocomplete="off" placeholder="请输入关联SN" />
+        </el-form-item>
+        <el-form-item :label-width="formLabelWidth">
+          <el-button v-if="isNew" color="#626aef" @click="AddUserName(ruleFormRef)">提交</el-button>
+          <el-button v-else color="#626aef" @click="AddUserName(ruleFormRef)">修改</el-button>
+        </el-form-item>
+      </el-form>
+    </Drawer>
+  </div>
 </template>
 
-<style scoped></style>
+<style scoped lang="scss">
+.IoTNetworkCard {
+  :deep(.el-drawer__header) {
+    margin-bottom: 0;
+  }
+  .input-suffix {
+    width: 100%;
+    .inline-flex {
+      white-space: nowrap;
+    }
+    .btn {
+      display: flex;
+      justify-content: end;
+    }
+    .w-50 {
+      width: 12.5rem;
+    }
+  }
+}
+</style>

+ 178 - 36
src/views/storehouse/ProductionList.vue

@@ -9,41 +9,43 @@ import {
 } from '@/api/storehouse/index'
 import { GlobalStore } from '@/stores/index'
 import { ref, reactive, nextTick, onMounted } from 'vue'
+import Upload from '@/components/Upload/index.vue'
 import Drawer from '@/components/Drawer/index.vue'
+import Dialog from '@/components/dialog/Dialog.vue'
 import TableBase from '@/components/TableBase/index.vue'
 import type { FormInstance, FormRules } from 'element-plus'
-import { Edit, Delete } from '@element-plus/icons-vue'
+import { Edit, Delete, Picture } from '@element-plus/icons-vue'
 import type { ColumnProps } from '@/components/TableBase/interface/index'
 import { ElNotification, ElMessageBox, ElMessage } from 'element-plus'
-import Upload from '@/components/Upload/index.vue'
 
 const isNew = ref(true)
 const globalStore = GlobalStore()
 const formLabelWidth = ref('100px')
+const ruleFormRef = ref<FormInstance>()
 const drawerRef = ref<InstanceType<typeof Drawer> | null>(null)
 const TableRef = ref<InstanceType<typeof TableBase> | null>(null)
-
-const initParam = {
-  User_tokey: globalStore.GET_User_tokey,
-  T_name: ''
-}
-
+const uploadRef = ref<InstanceType<typeof Upload> | null>(null)
 const columns: ColumnProps[] = [
   { type: 'index', label: '序号', width: 80 },
-  { prop: 'T_img', label: '产品图片' },
+  { prop: 'T_img', label: '产品图片', name: 'T_img' },
   { prop: 'T_name', label: '产品名称' },
   { prop: 'T_class_name', label: '产品分类' },
   { prop: 'T_model', label: '产品型号' },
   { prop: 'T_spec', label: '产品规格' },
-  { prop: 'T_relation_sn', label: '关联SN' },
+  { prop: 'T_relation_sn', label: '关联SN', name: 'T_relation_sn' },
   { prop: 'T_name', label: '更新时间' },
   { prop: 'operation', label: '操作', width: 150, fixed: 'right' }
 ]
 
 const rules = reactive<FormRules>({
-  name: [{ required: true, message: '请输入仓库名称', trigger: 'blur' }]
+  T_name: [{ required: true, message: '请输入产品名称', trigger: 'blur' }],
+  T_class: [{ required: true, message: '请选择产品分类', trigger: 'blur' }],
+  T_model: [{ required: true, message: '请输入产品型号', trigger: 'blur' }],
+  T_spec: [{ required: true, message: '请选择仓库规格', trigger: 'blur' }],
+  T_relation_sn: [{ required: true, message: '请选择关联SN', trigger: 'blur' }]
 })
 const form = reactive({
+  T_id: '',
   T_name: '',
   T_class: '',
   T_model: '',
@@ -55,32 +57,112 @@ const form = reactive({
 
 const openDrawer = (type: string, row?: any) => {
   isNew.value = type === 'new' ? true : false
-  // nextTick(() => {
-  //   !isNew.value && ((form.name = row.T_name), (form.id = row.Id))
-  // })
+  !specList.value.length && getSpecList()
+  nextTick(() => {
+    if (!isNew.value) {
+      form.T_id = row.Id
+      form.T_img = row.T_img
+      form.T_name = row.T_name
+      form.T_spec = row.T_spec
+      form.T_model = row.T_model
+      form.T_class = row.T_class
+      form.T_remark = row.T_remark
+      form.T_relation_sn = row.T_relation_sn
+    }
+    console.log(form)
+  })
   drawerRef.value?.openDrawer()
 }
-const UserDelete = (row: any) => {
-  //
+
+const AddProduction = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.validate(async valid => {
+    if (valid) {
+      let res: any = {}
+      if (isNew.value) {
+        res = await Storehouse_Product_Add({ User_tokey: globalStore.GET_User_tokey, ...form })
+      } else {
+        res = await Storehouse_Product_Edit({ User_tokey: globalStore.GET_User_tokey, ...form })
+      }
+      if (res.Code === 200) {
+        ElMessage.success(`产品${isNew.value ? '添加' : '修改'}成功!!`)
+        nextTick(() => {
+          drawerRef.value?.closeDrawer()
+          TableRef.value?.getTableList()
+          resetForm(ruleFormRef.value)
+          isNew.value = true
+        })
+      }
+    } else {
+      return false
+    }
+  })
 }
 
-const callbackDrawer = () => {}
+const ProductDelete = (row: any) => {
+  ElMessageBox.confirm('您确定要删除该产品吗?', '警告', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  })
+    .then(async () => {
+      const res: any = await Storehouse_Product_Del({ User_tokey: globalStore.GET_User_tokey, T_id: row.Id })
+      if (res.Code === 200) {
+        ElMessage.success('删除成功!')
+        nextTick(() => {
+          TableRef.value?.getTableList()
+        })
+      }
+    })
+    .catch(() => {
+      ElMessage.warning('取消成功!')
+    })
+}
 
+type Fn = () => void
+const callbackDrawer = (done: Fn) => {
+  resetForm(ruleFormRef.value)
+  done()
+}
+
+const resetForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  uploadRef.value?.clearfileList()
+  formEl.resetFields()
+}
 // 搜索
-const search = reactive({
+const initParam = reactive({
+  User_tokey: globalStore.GET_User_tokey,
   T_name: '',
-  T_class_name: ''
+  T_class: ''
 })
+
 const searchHandle = () => {
-  //
+  TableRef.value?.searchTable()
 }
 
+// 预览图片
+const url = ref('')
+const srcList = ref<any[]>([])
+const dialog = ref<InstanceType<typeof Dialog> | null>(null)
+const previewImg = (str: string) => {
+  dialog.value?.DialogOpen()
+  url.value = str
+  srcList.value.push(str)
+}
 // 获取产品分类
 const options = ref<any[]>([])
 const getProductClassList = async () => {
   const res: any = await Storehouse_ProductClass_List({ page: 1, page_z: 999 })
   options.value = res.Data.Data
 }
+// 获取产品规格
+const specList = ref<any[]>([])
+const getSpecList = async () => {
+  const res: any = await Storehouse_Product_Spec_List({ User_tokey: globalStore.GET_User_tokey })
+  console.log(res)
+  specList.value = res.Data
+}
 
 onMounted(() => {
   getProductClassList()
@@ -95,13 +177,19 @@ onMounted(() => {
           <el-row :gutter="20" style="margin-bottom: 0">
             <el-col :xl="6" :lg="8" :md="10">
               <span class="inline-flex items-center">产品分类:</span>
-              <el-select v-model="search.T_class_name" class="w-50 m-2" placeholder="请选择分类~">
+              <el-select v-model="initParam.T_class" class="w-50 m-2" clearable placeholder="请选择分类~">
                 <el-option v-for="item in options" :key="item.Id" :label="item.T_name" :value="item.Id" />
               </el-select>
             </el-col>
             <el-col :xl="10" :md="12">
               <span class="inline-flex items-center">产品名称:</span>
-              <el-input v-model="search.T_name" type="text" class="w-50 m-2" placeholder="按产品名称、产品型号搜索" />
+              <el-input
+                v-model="initParam.T_name"
+                type="text"
+                class="w-50 m-2"
+                placeholder="按产品名称、产品型号搜索"
+                @change="searchHandle"
+              />
               <el-button type="primary" @click="searchHandle">搜索</el-button>
             </el-col>
             <el-col :xl="6" :md="2" class="btn"
@@ -110,9 +198,29 @@ onMounted(() => {
           </el-row>
         </div>
       </template>
+      <template #T_img="{ row }">
+        <el-image
+          v-if="row.T_img"
+          style="width: 100px; height: 100px"
+          :src="row.T_img"
+          fit="cover"
+          @click="previewImg(row.T_img)"
+        />
+        <el-image v-else style="width: 100px; height: 100px">
+          <template #error>
+            <div class="image-slot">
+              <el-icon><Picture /></el-icon>
+            </div>
+          </template>
+        </el-image>
+      </template>
+      <template #T_relation_sn="{ row }">
+        <el-tag v-if="row.T_relation_sn === 1" effect="dark">是</el-tag>
+        <el-tag v-else type="success" effect="dark">否</el-tag>
+      </template>
       <template #right="{ row }">
         <el-button link type="primary" size="small" :icon="Edit" @click="openDrawer('edit', row)">编辑</el-button>
-        <el-button link type="danger" size="small" :icon="Delete" @click="UserDelete(row)">删除</el-button>
+        <el-button link type="danger" size="small" :icon="Delete" @click="ProductDelete(row)">删除</el-button>
       </template>
     </TableBase>
     <Drawer ref="drawerRef" :handleClose="callbackDrawer">
@@ -121,37 +229,71 @@ onMounted(() => {
       </template>
       <el-form ref="ruleFormRef" :model="form" :rules="rules">
         <el-divider border-style="dashed" />
-        <el-form-item label="产品名称:" class="m-b-6" :label-width="formLabelWidth" prop="name">
+        <el-form-item label="产品名称:" class="m-b-6" :label-width="formLabelWidth" prop="T_name">
           <el-input v-model="form.T_name" type="text" autocomplete="off" placeholder="请输入产品名称" />
         </el-form-item>
-        <el-form-item label="产品分类:" class="m-b-6" :label-width="formLabelWidth" prop="name">
+        <el-form-item label="产品分类:" class="m-b-6" :label-width="formLabelWidth" prop="T_class">
           <el-select v-model="form.T_class" placeholder="请选择产品分类~">
             <el-option v-for="item in options" :key="item.Id" :label="item.T_name" :value="item.Id" />
           </el-select>
         </el-form-item>
-        <el-form-item label="产品型号:" class="m-b-6" :label-width="formLabelWidth" prop="name">
-          <el-input v-model="form.T_name" type="text" autocomplete="off" placeholder="请输入产品型号" />
+        <el-form-item label="产品型号:" class="m-b-6" :label-width="formLabelWidth" prop="T_model">
+          <el-input v-model="form.T_model" type="text" autocomplete="off" placeholder="请输入产品型号" />
         </el-form-item>
-        <el-form-item label="产品规格:" class="m-b-6" :label-width="formLabelWidth" prop="name">
-          <el-select v-model="form.T_class" placeholder="请选择产品规格~">
-            <el-option v-for="item in options" :key="item.Id" :label="item.T_name" :value="item.Id" />
+        <el-form-item label="产品规格:" class="m-b-6" :label-width="formLabelWidth" prop="T_spec">
+          <el-select v-model="form.T_spec" placeholder="请选择产品规格~">
+            <el-option v-for="item in specList" :key="item.Id" :label="item.T_name" :value="item.T_name" />
           </el-select>
         </el-form-item>
-        <el-form-item label="产品图片:" class="m-b-6" :label-width="formLabelWidth" prop="name">
-          <Upload></Upload>
+        <el-form-item label="关联SN:" class="m-b-6" :label-width="formLabelWidth" prop="T_relation_sn">
+          <el-radio-group v-model="form.T_relation_sn" class="ml-4">
+            <el-radio :label="1">是</el-radio>
+            <el-radio :label="0">否</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="产品图片:" class="m-b-6" :label-width="formLabelWidth" prop="T_img">
+          <!-- <Upload ref="uploadRef" v-if="isNew || form.T_img" :isImg="true" :limit="1" v-model="form.T_img"></Upload> -->
+          <Upload ref="uploadRef" :isImg="true" :limit="1" v-model="form.T_img"></Upload>
+        </el-form-item>
+        <el-form-item label="备注:" class="m-b-6" :label-width="formLabelWidth" prop="T_remark">
+          <el-input
+            v-model="form.T_remark"
+            :autosize="{ minRows: 4, maxRows: 6 }"
+            type="textarea"
+            placeholder="请输入备注信息"
+          />
+        </el-form-item>
+        <el-form-item :label-width="formLabelWidth">
+          <el-button v-if="isNew" color="#626aef" @click="AddProduction(ruleFormRef)">提交</el-button>
+          <el-button v-else color="#626aef" @click="AddProduction(ruleFormRef)">修改</el-button>
         </el-form-item>
-        <!-- <el-form-item :label-width="formLabelWidth">
-          <el-button v-if="isNew" color="#626aef" @click="AddUserName(ruleFormRef)">提交</el-button>
-          <el-button v-else color="#626aef" @click="AddUserName(ruleFormRef)">修改</el-button>
-        </el-form-item> -->
       </el-form>
     </Drawer>
+    <Dialog ref="dialog" width="50%">
+      <el-image :src="url" :zoom-rate="1.2" :preview-src-list="srcList" fit="cover" />
+    </Dialog>
   </div>
 </template>
 
 <style scoped lang="scss">
 .production-list {
   height: 100%;
+  .el-image {
+    cursor: pointer;
+  }
+  .image-slot {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 100%;
+    height: 100%;
+    background: var(--el-fill-color-light);
+    color: var(--el-text-color-secondary);
+    font-size: 30px;
+  }
+  .image-slot .el-icon {
+    font-size: 30px;
+  }
   .input-suffix {
     width: 100%;
     .inline-flex {