Ver código fonte

add:新增上传接口,对接表单上传

zlong 1 ano atrás
pai
commit
6120d0b8fa

+ 3 - 2
components.d.ts

@@ -16,7 +16,6 @@ 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']
@@ -25,11 +24,13 @@ declare module 'vue' {
     TIcon: typeof import('tdesign-vue-next')['Icon']
     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']
+    TRadioGroup: typeof import('tdesign-vue-next')['RadioGroup']
     TSpace: typeof import('tdesign-vue-next')['Space']
     TTable: typeof import('tdesign-vue-next')['Table']
     TTag: typeof import('tdesign-vue-next')['Tag']
+    TUpload: typeof import('tdesign-vue-next')['Upload']
   }
 }

+ 1 - 2
src/api/carousal.ts

@@ -1,8 +1,7 @@
 import httpClient from './httpClient'
 import type { CreateCarousalsRequest, GetCarousalsRequest } from '@/model/carousals'
-import type { UnwrapNestedRefs } from 'vue'
 
-export const createCarousal = (createCarousalsRequest: UnwrapNestedRefs<{ size: number; page: number }> & {}) => {
+export const createCarousal = (createCarousalsRequest: CreateCarousalsRequest) => {
   return httpClient.post('/carousals', createCarousalsRequest)
 }
 

+ 31 - 3
src/components/ImageUpload.vue

@@ -1,7 +1,33 @@
 <script setup lang="ts">
-import type { UploadProps } from 'tdesign-vue-next'
+import { MessagePlugin, type UploadProps } from 'tdesign-vue-next'
+import {ref} from 'vue'
+import { useAppStore } from '@/stores/app'
+const imageViewerProps = ref<UploadProps['imageViewerProps']>({
+  closeOnEscKeydown: false,
+});
+const sizeLimit = ref<UploadProps['sizeLimit']>({
+  size: 2,
+  unit: 'MB',
+});
 const file1 = ref<UploadProps['value']>([]);
+const disabled = ref(false);
+const autoUpload = ref(true);
+const showImageFileName = ref(true);
+const uploadAllFilesInOneRequest = ref(false);
+const handleFail: UploadProps['onFail'] = ({ file }) => {
+  MessagePlugin.error(`文件 ${file.name} 上传失败`);
+};
+const handleSuccess: UploadProps['onSuccess'] = ({ file }) => {
+  if(file?.url){
+    emits('on-success',file.url)
+  }
+}
+const appStore = useAppStore()
 
+const uploadHeaders = ref<UploadProps['headers']>({
+  Authorization: `Bearer ${appStore.token}`
+})
+const emits = defineEmits(['on-success'])
 </script>
 
 <template>
@@ -11,9 +37,10 @@ const file1 = ref<UploadProps['value']>([]);
       v-model="file1"
       :image-viewer-props="imageViewerProps"
       :size-limit="sizeLimit"
-      action="https://service-bv448zsw-1257786608.gz.apigw.tencentcs.com/api/upload-demo"
+      action="/api/files/upload"
+      :headers="uploadHeaders"
       theme="image"
-      tips="单张图片文件上传(上传成功状态演示)"
+      tips="图片上传限制为2M"
       accept="image/*"
       :disabled="disabled"
       :auto-upload="autoUpload"
@@ -25,6 +52,7 @@ const file1 = ref<UploadProps['value']>([]);
           },
         }"
       @fail="handleFail"
+      @success="handleSuccess"
     >
       <!-- custom UI -->
       <!-- <template #fileListDisplay="{ files }">

+ 2 - 2
src/model/carousals.ts

@@ -1,8 +1,8 @@
 export interface CreateCarousalsRequest {
   imageUrl:string,
   targetType:string,
-  targetUrl:string,
-  targetId:string
+  targetUrl?:string,
+  targetId?:string
 }
 export interface GetCarousalsRequest {
   page:number,

+ 120 - 95
src/pages/carousal/components/CarousalDialog.vue

@@ -1,6 +1,6 @@
 <template>
   <t-dialog
-    width="40%"
+    width="70%"
     :header="header"
     :confirmBtn="confirmBtn"
     :closeOnOverlayClick="false"
@@ -8,15 +8,20 @@
     @confirm="fetchSaveClassifyData"
   >
     <t-space direction="vertical" style="width: 100%">
-      <TForm ref="form" :data="classifyData" :rules="rules" resetType="initial">
-        <TFormItem label="分类名称:" name="name" help="名称长度小于5个汉字,大于2个汉字">
-          <t-input v-model.trim="classifyData.name" clearable placeholder="请输入分类名称" />
+      <TForm ref="form" :data="carousalData" :rules="rules" resetType="initial">
+        <TFormItem label="轮播图:" name="imageUrl">
+          <ImageUpload @on-success="handleSuccessImg"/>
         </TFormItem>
-        <TFormItem label="分类编号:" name="numbering" help="编号由大于4个字符的英文字母组成">
-          <t-input v-model.trim="classifyData.numbering" clearable placeholder="请输入分类编号" />
+        <TFormItem label="跳转类型:" name="targetType" >
+          <t-radio-group :default-value="carousalData.targetType" v-model="carousalData.targetType">
+            <t-radio-button v-for="(item,index) in typeOption" :key="index" :value="item.value">{{item.label}}</t-radio-button>
+          </t-radio-group>
         </TFormItem>
-        <TFormItem label="排序级别:" name="sort" help="排序级别由正整数组成">
-          <t-input v-model.trim="classifyData.sort" clearable placeholder="请输入排序级别" />
+        <TFormItem label="跳转文章路径:" name="targetId" v-if="carousalData.targetType === 'article' ">
+          <t-input v-model.trim="carousalData.targetId" clearable placeholder="请输入文章id" />
+        </TFormItem>
+        <TFormItem label="跳转网址路径:" name="targetUrl" v-else-if="carousalData.targetType === 'url' ">
+          <t-input v-model.trim="carousalData.targetUrl" clearable placeholder="请输入网址路径" />
         </TFormItem>
       </TForm>
     </t-space>
@@ -26,6 +31,8 @@
 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'
 const props = defineProps<{
   isEdit?: Boolean | null
   headerTitle?: String | null
@@ -34,97 +41,107 @@ const props = defineProps<{
 const form = ref<FormInstanceFunctions | null>(null)
 const header = computed(() => `${props.isEdit ? '编辑' : '创建'}${props.headerTitle || ''}`)
 const confirmBtn = computed(() => (props.isEdit ? '保存' : '确定'))
-const classifyData: FormProps['data'] = reactive({ name: '', sort: 0 })
-const rules: FormProps['rules'] = {
-  name: [
-    {
-      required: true,
-      message: '分类名称不能为空',
-      type: 'error',
-      trigger: 'blur'
-    },
-    {
-      required: true,
-      message: '分类名称不能为空',
-      type: 'error',
-      trigger: 'change'
-    },
-    {
-      whitespace: true,
-      message: '分类名称不能为空'
-    },
-    {
-      min: 4,
-      message: '输入汉字长度应在2到5之间',
-      type: 'error',
-      trigger: 'blur'
-    },
-    {
-      max: 10,
-      message: '输入汉字长度应在2到5之间',
-      type: 'error',
-      trigger: 'blur'
-    }
-  ],
-  numbering: [
-    {
-      required: true,
-      message: '分类编号不能为空',
-      type: 'error',
-      trigger: 'blur'
-    },
-    {
-      required: true,
-      message: '分类编号不能为空',
-      type: 'error',
-      trigger: 'change'
-    },
-    {
-      whitespace: true,
-      message: '分类编号不能为空'
-    },
-    {
-      min: 4,
-      message: '输入字符应大于4个字符长度',
-      type: 'error',
-      trigger: 'blur'
-    },
-    {
-      pattern: /^[A-Za-z]+$/,
-      message: '输入字符须为英文字母',
-      type: 'error',
-      trigger: 'blur'
-    }
-  ],
-  sort: [
-    {
-      required: true,
-      message: '排序级别不能为空',
-      type: 'error',
-      trigger: 'blur'
-    },
-    {
-      required: true,
-      message: '排序级别不能为空',
-      type: 'error',
-      trigger: 'change'
-    },
-    {
-      whitespace: true,
-      message: '排序级别不能为空'
-    },
-    {
-      pattern: /^[1-9]\d*$/,
-      message: '排序级别必须为正整数',
-      type: 'error',
-      trigger: 'blur'
-    }
-  ]
+const carousalData: FormProps['data'] = reactive({})
+const rules = computed(() => {
+  const commonRules = {
+    imageUrl: [
+      {
+        required: true,
+        message: '轮播图必须是上传',
+        type: 'error',
+        trigger: 'change',
+      },
+    ],
+    targetType: [
+      {
+        required: true,
+        message: '请选择跳转类型',
+        type: 'error',
+        trigger: 'blur',
+      },
+      {
+        required: true,
+        message: '请选择跳转类型',
+        type: 'error',
+        trigger: 'change',
+      },
+    ],
+  };
+  return carousalData.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
 }
+const typeOption = reactive([
+  { value: 'article', label: '文章' },
+  { value: 'url', label: '地址' },
+])
 watch(
   () => props.classify,
   (newClassify) => {
-    Object.assign(classifyData, newClassify)
+    Object.assign(carousalData, newClassify)
   },
   { deep: true }
 )
@@ -132,8 +149,16 @@ const fetchSaveClassifyData = async () => {
   /* TODO: 保存分类数据 */
   if (form.value) {
     const valid = await form.value.validate()
+    // 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
+      }
+      const createCarousalApi = createCarousal(carousalData)
+      console.log(createCarousalApi,'createCarousalApi')
       return
     }
   }

+ 1 - 1
src/pages/carousal/index.vue

@@ -100,7 +100,7 @@ const onPageChange = () => {
       :classify="currentTableData"
       v-model:visible="classifyDialogVisible"
       :isEdit="isEdit"
-      headerTitle="分类"
+      headerTitle="轮播图"
     ></ClassifyDialog>
   </RegularPage>
 </template>

+ 8 - 8
src/router/index.ts

@@ -45,13 +45,13 @@ const router = createRouter({
   ]
 })
 
-// router.beforeEach((to, from, next) => {
-//   if (to.name !== 'login') {
-//     const appStore = useAppStore()
-//     appStore.isLogin ? next() : next({ name: 'login' })
-//     return
-//   }
-//   next()
-// })
+router.beforeEach((to, from, next) => {
+  if (to.name !== 'login') {
+    const appStore = useAppStore()
+    appStore.isLogin ? next() : next({ name: 'login' })
+    return
+  }
+  next()
+})
 
 export default router