浏览代码

add:增加编辑、删除等功能

zlong 1 年之前
父节点
当前提交
b4541b6f78
共有 5 个文件被更改,包括 171 次插入91 次删除
  1. 3 0
      components.d.ts
  2. 3 3
      src/api/carousal.ts
  3. 22 1
      src/components/ImageUpload.vue
  4. 48 76
      src/pages/carousal/components/CarousalDialog.vue
  5. 95 11
      src/pages/carousal/index.vue

+ 3 - 0
components.d.ts

@@ -16,14 +16,17 @@ declare module 'vue' {
     TAvatar: typeof import('tdesign-vue-next')['Avatar']
     TButton: typeof import('tdesign-vue-next')['Button']
     TCard: typeof import('tdesign-vue-next')['Card']
+    TCheckbox: typeof import('tdesign-vue-next')['Checkbox']
     TDialog: typeof import('tdesign-vue-next')['Dialog']
     TDropdown: typeof import('tdesign-vue-next')['Dropdown']
     TForm: typeof import('tdesign-vue-next')['Form']
     TFormItem: typeof import('tdesign-vue-next')['FormItem']
     THeader: typeof import('tdesign-vue-next')['Header']
     TIcon: typeof import('tdesign-vue-next')['Icon']
+    TImageViewer: typeof import('tdesign-vue-next')['ImageViewer']
     TInput: typeof import('tdesign-vue-next')['Input']
     TLayout: typeof import('tdesign-vue-next')['Layout']
+    TLink: typeof import('tdesign-vue-next')['Link']
     TMenu: typeof import('tdesign-vue-next')['Menu']
     TMenuItem: typeof import('tdesign-vue-next')['MenuItem']
     TRadioButton: typeof import('tdesign-vue-next')['RadioButton']

+ 3 - 3
src/api/carousal.ts

@@ -16,14 +16,14 @@ export const updateCarousalItem = (id:number,updateCarousalsRequest: CreateCarou
   return httpClient.put(`/carousals/${id}`,updateCarousalsRequest)
 }
 
-export const deleteCarousal = (id:number) => {
+export const deleteCarousal = (id:string | number) => {
   return httpClient.delete(`/carousals/${id}`)
 }
 
-export const activeCarousal = (id: { id: string | number }) => {
+export const activeCarousal = (id: string | number) => {
   return httpClient.put(`/carousals/${id}/active`)
 }
 
-export const inactiveCarousal = (id: { id: string | number }) => {
+export const inactiveCarousal = (id: string | number ) => {
   return httpClient.put(`/carousals/${id}/inactive`)
 }

+ 22 - 1
src/components/ImageUpload.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import { MessagePlugin, type UploadProps } from 'tdesign-vue-next'
-import {ref} from 'vue'
+import { ref, watch } from 'vue'
 import { useAppStore } from '@/stores/app'
 const imageViewerProps = ref<UploadProps['imageViewerProps']>({
   closeOnEscKeydown: false,
@@ -19,6 +19,7 @@ const handleFail: UploadProps['onFail'] = ({ file }) => {
 };
 const handleSuccess: UploadProps['onSuccess'] = ({ file }) => {
   if(file?.url){
+    console.log(file1.value,'file1')
     emits('on-success',file.url)
   }
 }
@@ -27,7 +28,27 @@ const appStore = useAppStore()
 const uploadHeaders = ref<UploadProps['headers']>({
   Authorization: `Bearer ${appStore.token}`
 })
+const props = defineProps({
+  imageUrl:{
+    type:Object,
+    default:() => {
+      return { url: '' }
+    }
+  }
+})
 const emits = defineEmits(['on-success'])
+watch(() => props.imageUrl,(newVal) => {
+  if(newVal){
+    if(newVal.url !== ''){
+      file1.value = [{ ...newVal }]
+    }else{
+      file1.value = []
+    }
+  }
+},{
+  deep:true,
+  immediate:true
+})
 </script>
 
 <template>

+ 48 - 76
src/pages/carousal/components/CarousalDialog.vue

@@ -8,9 +8,9 @@
     @confirm="fetchSaveClassifyData"
   >
     <t-space direction="vertical" style="width: 100%">
-      <TForm ref="form" :data="carousalData" :rules="rules" resetType="initial">
+      <TForm ref="form" :data="carousalData" :rules="rules" resetType="initial" label-width="100">
         <TFormItem label="轮播图:" name="imageUrl">
-          <ImageUpload @on-success="handleSuccessImg"/>
+          <ImageUpload @on-success="handleSuccessImg" :image-url="imgUrl"/>
         </TFormItem>
         <TFormItem label="跳转类型:" name="targetType" >
           <t-radio-group :default-value="carousalData.targetType" v-model="carousalData.targetType">
@@ -32,17 +32,19 @@ import type { Classify } from '@/model/classify'
 import { computed, ref, reactive, watch } from 'vue'
 import type { FormInstanceFunctions, FormProps } from 'tdesign-vue-next'
 import ImageUpload from '@/components/ImageUpload.vue'
-import { createCarousal } from '@/api/carousal'
+import { createCarousal, updateCarousalItem } from '@/api/carousal'
 const props = defineProps<{
   isEdit?: Boolean | null
   headerTitle?: String | null
-  classify: Classify | {}
-  count:number
+  classify: Classify | {} | number
 }>()
+const emit = defineEmits(['success'])
 const form = ref<FormInstanceFunctions | null>(null)
 const header = computed(() => `${props.isEdit ? '编辑' : '创建'}${props.headerTitle || ''}`)
+const formType = computed(() => `${props.isEdit ? 'update' : 'add'}`)
 const confirmBtn = computed(() => (props.isEdit ? '保存' : '确定'))
-const carousalData: FormProps['data'] = reactive({})
+const carousalData: FormProps['data'] = ref({})
+const imgUrl = ref({})
 const rules = computed(() => {
   const commonRules = {
     imageUrl: [
@@ -68,82 +70,32 @@ const rules = computed(() => {
       },
     ],
   };
-  return carousalData.targetType === 'article'
+  return carousalData.value.targetType === 'article'
     ? {...commonRules, targetId: [{ required: true, message: '文章 id 不能为空', type: 'error', trigger: 'blur' }, { required: true, message: '文章 id 不能为空', type: 'error', trigger: 'change' }, { whitespace: true, message: '网址路径不能为空' }] }
     : {...commonRules, targetUrl: [{ required: true, message: '网址路径不能为空', type: 'error', trigger: 'blur' }, { required: true, message: '网址路径不能为空', type: 'error', trigger: 'change' }, { whitespace: true, message: '网址路径不能为空' }] };
 });
-// const rules: FormProps['rules'] = {
-//   imageUrl:[
-//     {
-//       required: true,
-//       message:'轮播图必须是上传',
-//       type:'error',
-//       trigger: 'change'
-//     }
-//   ],
-//   targetType:[
-//     {
-//       required: true,
-//       message: '请选择跳转类型',
-//       type: 'error',
-//       trigger: 'blur'
-//     },
-//     {
-//       required: true,
-//       message: '请选择跳转类型',
-//       type: 'error',
-//       trigger: 'change'
-//     },
-//   ],
-//   targetId:[
-//     {
-//       required: true,
-//       message: '文章id不能为空',
-//       type: 'error',
-//       trigger: 'blur'
-//     },
-//     {
-//       required: true,
-//       message: '文章id不能为空',
-//       type: 'error',
-//       trigger: 'change'
-//     },
-//     {
-//       whitespace: true,
-//       message: '网址路径不能为空'
-//     },
-//   ],
-//   targetUrl:[
-//     {
-//       required: true,
-//       message: '网址路径不能为空',
-//       type: 'error',
-//       trigger: 'blur'
-//     },
-//     {
-//       required: true,
-//       message: '网址路径不能为空',
-//       type: 'error',
-//       trigger: 'change'
-//     },
-//     {
-//       whitespace: true,
-//       message: '网址路径不能为空'
-//     },
-//   ],
-// }
 const handleSuccessImg = (res:string) => {
-  carousalData.imageUrl = res
+  console.log(res,'图片地址')
+  carousalData.value.imageUrl = res
 }
 const typeOption = reactive([
   { value: 'article', label: '文章' },
   { value: 'url', label: '地址' },
 ])
+const handleFormTypeData = () => {
+  if (carousalData.value.targetType === 'article') {
+    delete carousalData.value.targetUrl
+  } else if (carousalData.value.targetType === 'url') {
+    delete carousalData.value.targetId
+  }
+}
+
 watch(
   () => props.classify,
   (newClassify) => {
-    Object.assign(carousalData, newClassify)
-    console.log(carousalData)
+    carousalData.value = Object.assign({}, newClassify)
+    const carousalObj = Object.assign({},newClassify)
+    imgUrl.value.url = carousalObj.imageUrl || ''
   },
   {
     deep: true,
@@ -157,13 +109,33 @@ const fetchSaveClassifyData = async () => {
     // console.log(valid,'valid')
     if (valid && typeof valid === 'boolean') {
       /* TODO: 校验通过保存分类数据 */
-      if (carousalData.targetType === 'article') {
-        delete carousalData.targetUrl
-      } else if (carousalData.targetType === 'url') {
-        delete carousalData.targetId
+      handleFormTypeData()
+      // console.log(formType.value,'formType')
+      let res
+      switch (formType.value){
+        case 'create':
+          res = await createCarousal(carousalData.value)
+          emit('success', true)
+          break;
+        case 'update':
+          const fieldsToKeep = ["imageUrl", "targetType", "targetUrl", "targetId"];
+          const requestObj = {};
+
+          for (const field of fieldsToKeep) {
+            if (carousalData.value[field]!== null && carousalData.value[field]!== undefined) {
+              requestObj[field] = carousalData.value[field];
+            }
+          }
+          handleFormTypeData()
+          res = await updateCarousalItem(carousalData.value.id,requestObj)
+          emit('success', res)
+          break;
+        default:
+          res = await createCarousal(carousalData.value)
+          break;
       }
-      const createCarousalApi = createCarousal(carousalData)
-      console.log(createCarousalApi,'createCarousalApi')
+      // const createCarousalApi = createCarousal(carousalData.value)
+      // console.log(createCarousalApi,'createCarousalApi')
       return
     }
   }

+ 95 - 11
src/pages/carousal/index.vue

@@ -4,18 +4,20 @@ import { onMounted, ref, reactive } from 'vue'
 import type { BaseTableColumns } from 'tdesign-vue-next'
 import type { Classify } from '@/model/classify'
 import ClassifyDialog from '@/pages/carousal/components/CarousalDialog.vue'
-import { activeCarousal, getCarousalList, inactiveCarousal } from '@/api/carousal'
+import { activeCarousal, deleteCarousal, getCarousalList, inactiveCarousal } from '@/api/carousal'
+import { BrowseIcon } from 'tdesign-icons-vue-next'
+import { aS } from 'vitest/dist/reporters-yx5ZTtEV'
 
 const columns: BaseTableColumns = [
   {
     title: 'ID',
     colKey: 'id',
-    width: 100
+    width: 100,
   },
   {
     title: '轮播图',
     colKey: 'imageUrl',
-    width: 100
+    width: 100,
   },
   {
     title: '显示状态',
@@ -25,7 +27,7 @@ const columns: BaseTableColumns = [
   {
     title: '操作',
     colKey: 'operation',
-    width: 100
+    width: 100,
   }
 ]
 const loading = ref(false)
@@ -53,27 +55,41 @@ const fetchClassifyTableData = async () => {
 onMounted(fetchClassifyTableData)
 const editClick = (row:object) => {
   isEdit.value = true
-  currentTableData.value = row
+  currentTableData.value = Object.assign({}, row)
   carousalDialogVisible.value = true
 }
 const showClick = async (res:object) => {
   if (res.status === 'active'){
-    await inactiveCarousal({ id: res.id })
+    await inactiveCarousal(res.id)
   } else {
-    await activeCarousal({ id: res.id })
+    await activeCarousal(res.id)
   }
   await fetchClassifyTableData()
-  console.log(res)
+}
+const delClick = async (row:object) => {
+  console.log(row,'删除')
+  await deleteCarousal(row.id)
+  await fetchClassifyTableData()
+}
+const handleCreateCarousal = () => {
+  isEdit.value = false
+  carousalDialogVisible.value = true
+  currentTableData.value = {}
 }
 const onPageChange = () => {
   /* TODO: 分页切换 */
 }
+const handleSuccessDialog = async (res) => {
+  if (res){
+    await fetchClassifyTableData()
+  }
+}
 </script>
 
 <template>
   <RegularPage title="轮播图管理">
     <template #right-area>
-      <TButton @click="carousalDialogVisible = true">创建轮播图</TButton>
+      <TButton @click="handleCreateCarousal">创建轮播图</TButton>
     </template>
     <TTable
       class="w-full h-full"
@@ -92,6 +108,20 @@ const onPageChange = () => {
         onChange: onPageChange
       }"
     >
+      <template #imageUrl="{row}">
+        <div class="tdesign-demo-image-viewer__base">
+          <t-image-viewer :images="[row.imageUrl]">
+            <template #trigger="{ open }">
+              <div class="tdesign-demo-image-viewer__ui-image">
+                <img alt="test" :src="row.imageUrl" class="tdesign-demo-image-viewer__ui-image--img" />
+                <div class="tdesign-demo-image-viewer__ui-image--hover" @click="open">
+                  <span><BrowseIcon size="1.4em" /> 预览</span>
+                </div>
+              </div>
+            </template>
+          </t-image-viewer>
+        </div>
+      </template>
       <template #status="{row}">
         <TSpace>
           <TTag :theme="row.status === 'active' ? 'success' : 'danger'" variant="light">{{row.status === 'active' ? '显示中' : '已关闭'}}</TTag>
@@ -100,19 +130,21 @@ const onPageChange = () => {
       <template #roles="{ row }">
         <TSpace>
           <TTag v-for="role in row.roles" :key="role.id" theme="success" variant="light">{{
-            role.label
-          }}</TTag>
+              role.label
+            }}</TTag>
         </TSpace>
       </template>
       <template #operation="{ row }">
         <TButton variant="text" size="small" theme="primary" @click="editClick(row)">编辑</TButton>
         <TButton variant="text" size="small" theme="primary" @click="showClick(row)">{{ row.status === 'active' ? '关闭显示' : '开启显示'}}</TButton>
+        <TButton variant="text" size="small" theme="danger" @click="delClick(row)">删除</TButton>
       </template>
     </TTable>
     <ClassifyDialog
       :classify="currentTableData"
       v-model:visible="carousalDialogVisible"
       :isEdit="isEdit"
+      @success="handleSuccessDialog"
       headerTitle="轮播图"
     ></ClassifyDialog>
   </RegularPage>
@@ -123,4 +155,56 @@ const onPageChange = () => {
   flex: 1;
   min-height: 0;
 }
+.tdesign-demo-image-viewer__ui-image {
+  width: 100%;
+  height: 100%;
+  display: inline-flex;
+  position: relative;
+  justify-content: center;
+  align-items: center;
+  border-radius: var(--td-radius-small);
+  overflow: hidden;
+}
+
+.tdesign-demo-image-viewer__ui-image--hover {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  position: absolute;
+  left: 0;
+  top: 0;
+  opacity: 0;
+  background-color: rgba(0, 0, 0, 0.6);
+  color: var(--td-text-color-anti);
+  line-height: 22px;
+  transition: 0.2s;
+}
+
+.tdesign-demo-image-viewer__ui-image:hover .tdesign-demo-image-viewer__ui-image--hover {
+  opacity: 1;
+  cursor: pointer;
+}
+
+.tdesign-demo-image-viewer__ui-image--img {
+  width: auto;
+  height: auto;
+  max-width: 100%;
+  max-height: 100%;
+  cursor: pointer;
+  position: absolute;
+}
+
+
+.tdesign-demo-image-viewer__ui-image--icons .tdesign-demo-icon {
+  cursor: pointer;
+}
+
+.tdesign-demo-image-viewer__base {
+  width: 100px;
+  height: 100px;
+  border: 4px solid var(--td-bg-color-secondarycontainer);
+  border-radius: var(--td-radius-medium);
+}
 </style>