tangning 2 rokov pred
rodič
commit
57515a91e3

+ 2 - 1
.vscode/settings.json

@@ -139,5 +139,6 @@
     "brotli",
     "tailwindcss",
     "sider"
-  ]
+  ],
+  "vue-i18n.i18nPaths": "src\\locales,src\\locales\\lang,dist\\resource\\tinymce\\langs,public\\resource\\tinymce\\langs"
 }

+ 38 - 2
src/api/product/index.ts

@@ -20,7 +20,10 @@ enum Api {
   appFileUpload = '/service/manage/appFile/upload',
   spaceSdkDelete = '/service/manage/spaceSdk/delete',
   appFileList = '/service/manage/appFile/list',
-  appFileDelete = '/service/manage/appFile/delete'
+  appFileDelete = '/service/manage/appFile/delete',
+  tipList = '/service/manage/serviceUpTip/list',
+  tipUpdata = '/service/manage/serviceUpTip/saveOrUpdate',
+  tipDelete = '/service/manage/serviceUpTip/delete'
 }
 
 /**
@@ -211,4 +214,37 @@ export function AppFileUpload(
     },
     params,
   );
- }
+ }
+
+  
+export const tipList = (params) =>
+defHttp.post<Result>({
+  url: Api.tipList,
+  params,
+  headers: {
+    // @ts-ignore
+    ignoreCancelToken: true,
+  },
+});
+
+ 
+export const tipDelete = (params) =>
+defHttp.post<Result>({
+  url: Api.tipDelete,
+  params,
+  headers: {
+    // @ts-ignore
+    ignoreCancelToken: true,
+  },
+});
+
+ 
+export const tipUpdata = (params) =>
+defHttp.post<Result>({
+  url: Api.tipUpdata,
+  params,
+  headers: {
+    // @ts-ignore
+    ignoreCancelToken: true,
+  },
+});

+ 110 - 40
src/views/product/updataTips/AddModal.vue

@@ -7,6 +7,7 @@
     @visible-change="handleVisibleChange"
     @cancel="resetFields"
     :width="550"
+    :min-height="680"
     @ok="handleSubmit"
   >
     <div class="pt-2px pr-3px" style="width:600px">
@@ -23,7 +24,7 @@
   import { BasicModal, useModalInner } from '/@/components/Modal';
   import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
   import { useMessage } from '/@/hooks/web/useMessage';
-  import { uploadApi, SpaceSdkUpload } from '/@/api/product/index';
+  import { uploadApi, tipUpdata } from '/@/api/product/index';
   import { useI18n } from '/@/hooks/web/useI18n';
   import { isEmojiCharacter } from '/@/utils';
   const { t } = useI18n();
@@ -37,33 +38,37 @@
       const modelRef = ref({});
       const loading = ref(false)
       const fileFlow = reactive({
-        file:null
+        type:null
       })
       const { createMessage } = useMessage();
       const schemas: FormSchema[] = [
         {
-          field: 'type',
+          field: 'version',
           component: 'Input',
-          label: t('routes.product.types'),
+          label: '版本号',
+          required: true,
+          componentProps: {
+            maxLength: 15,
+          },
           colProps: {
             span: 20,
           },
         },{
-          field: 'ctivated',
+          field: 'timeList',
           label: '提示时段',
           component: 'RangePicker',
+          required: true,
           componentProps: {
             format: 'YYYY-MM-DD',
             valueFormat:'YYYY-MM-DD',
             width:100,
           },
-          colProps: {
-            xl: 6,
-            xxl: 6,
+          colProps: {            
+            span: 20,
           },
         },
         {
-          field: 'version',
+          field: 'title',
           component: 'Input',
           label: '中文标题',
           required: true,
@@ -92,11 +97,11 @@
             },
           ],
           componentProps: {
-            maxLength: 15,
+            maxLength: 50,
           },
         },
         {
-          field: 'versions',
+          field: 'titleEn',
           component: 'Input',
           label: '英文标题',
           required: true,
@@ -125,15 +130,16 @@
             },
           ],
           componentProps: {
-            maxLength: 15,
+            maxLength: 50,
           },
         },
         {
-          field: 'imprintCh',
+          field: 'description',
           component: 'InputTextArea',
-          // required: true,
+          required: true,
           label: t('routes.product.description_zh'),
           componentProps: {
+            maxLength:500,
             rows:4,
           },
           colProps: {
@@ -141,11 +147,12 @@
           },
         },
         {
-          field: 'imprintEn',
+          field: 'descriptionEn',
           component: 'InputTextArea',
-          // required: true,
+          required: true,
           label: t('routes.product.description_en'),
           componentProps: {
+            maxLength:500,
             rows:4,
           },
           colProps: {
@@ -153,11 +160,9 @@
           },
         },
         {
-          field: 'file',
+          field: 'imageUrl',
           component: 'Upload',
-          label: '图片',
-          required: true,
-          rules: [{ required: true, message: t('common.uploadMessge') }],
+          label: '中文图片/视频',
           // helpMessage: t('routes.corporation.uploadHelp'),
           itemProps: {
             validateTrigger: 'onBlur',
@@ -166,14 +171,7 @@
             api: uploadApi,
             maxNumber: 1,
             maxSize: 1000,
-            fileFlow:true,
-            accept: ['zip','rar','png'],
-            afterFetch: function (data) {
-              console.log('uploadApi',data)
-              // Reflect.set(data, 'url', data.file);
-              fileFlow.file = data.file
-              return data;
-            },
+            accept: ['jpeg','jpg','png','mp4'],
           },
 
           colProps: {
@@ -181,12 +179,66 @@
           },
         },
         {
-          field: 'isTop',
-          component: 'Input',
-          label: '详情链接',
+          field: 'imageUrlEn',
+          component: 'Upload',
+          label: '英文图片/视频',
+          // helpMessage: t('routes.corporation.uploadHelp'),
+          itemProps: {
+            validateTrigger: 'onBlur',
+          },
           componentProps: {
-            maxLength: 15,
+            api: uploadApi,
+            maxNumber: 1,
+            maxSize: 1000,
+            accept: ['jpeg','jpg','png','mp4'],
+          },
+
+          colProps: {
+            span: 20,
+          },
+        },
+        {
+          field: 'infoUrl',
+          component: 'Input',
+          required: true,
+          label: '中文详情链接',
+          rules: [
+            {
+              required: true,
+              // @ts-ignore
+              validator: async (rule, value) => {
+                var reg=/(http|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:/~\+#\u4e00-\u9fa5]*[\w\-\@?^=%&/~\+#])/;
+                if (!reg.test(value)) {
+                  return Promise.reject(t('common.inputText')+'正确的地址');
+                }
+                return Promise.resolve();
+              },
+              trigger: 'change',
+            },
+          ],
+          colProps: {
+            span: 20,
           },
+        },
+        {
+          field: 'infoUrlEn',
+          component: 'Input',
+          required: true,
+          label: '英文详情链接',
+          rules: [
+            {
+              required: true,
+              // @ts-ignore
+              validator: async (rule, value) => {
+                var reg=/(http|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:/~\+#\u4e00-\u9fa5]*[\w\-\@?^=%&/~\+#])/;
+                if (!reg.test(value)) {
+                  return Promise.reject(t('common.inputText')+'正确的地址');
+                }
+                return Promise.resolve();
+              },
+              trigger: 'change',
+            },
+          ],
           colProps: {
             span: 20,
           },
@@ -235,9 +287,10 @@
       }
       function onDataReceive(data) {
         modelRef.value = data
+        fileFlow.type = data
         resetFields();
         setFieldsValue({
-          type:renderOwnTypeLabel(Number(data)),
+          typeText:renderOwnTypeLabel(Number(data)),
         });
       }
       const handleSubmit = async () => {
@@ -245,15 +298,13 @@
         try {
           const params = await validate();
           const apiData = {
-            data:{
-            ...params as any,
-            platformType:modelRef.value,
-            file:fileFlow.file,
-            isTop:params.isTop && params.isTop[0] || 0
-            }
+            ...params,
+            type:fileFlow.type,
+            imageUrl:params.imageUrl?.[0],
+            imageUrlEn:params.imageUrlEn?.[0],
           }
           console.log('res', apiData);
-          const res = await SpaceSdkUpload(apiData);
+          const res = await tipUpdata(apiData);
           console.log('res', res);
           closeModal();
           resetFields();
@@ -268,6 +319,25 @@
       function handleVisibleChange(v) {
         v && props.userData && nextTick(() => onDataReceive(props.userData));
       }
+      function IsURL(str_url){
+        var strRegex = "^((https|http|ftp|rtsp|mms)?://)" 
+        + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?" //ftp的user@ 
+          + "(([0-9]{1,3}.){3}[0-9]{1,3}" // IP形式的URL- 199.194.52.184 
+          + "|" // 允许IP和DOMAIN(域名)
+          + "([0-9a-z_!~*'()-]+.)*" // 域名- www. 
+          + "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]." // 二级域名 
+          + "[a-z]{2,6})" // first level domain- .com or .museum 
+          + "(:[0-9]{1,4})?" // 端口- :80 
+          + "((/?)|" // a slash isn't required if there is no file name 
+          + "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$"; 
+          var re=new RegExp(strRegex); 
+        //re.test()
+          if (re.test(str_url)){
+          return (true); 
+          }else{ 
+          return (false); 
+          }
+        }
       return {
         register,
         rendercameraTypeLabel,

+ 170 - 70
src/views/product/updataTips/EditModal.vue

@@ -2,22 +2,19 @@
   <BasicModal
     v-bind="$attrs"
     @register="register"
+    title="编辑"
     :confirmLoading="loading"
-    title="编辑版本"
     @visible-change="handleVisibleChange"
     @cancel="resetFields"
+    :width="550"
+    :min-height="680"
     @ok="handleSubmit"
   >
-    <div class="pt-2px pr-3px">
+    <div class="pt-2px pr-3px" style="width:600px">
       <BasicForm @register="registerForm" :model="model" >
         <template #text="{ model, field }">
           {{ model[field]  }}
         </template>
-        <template #Checkbox="{ model, field }">
-          <div>
-            <Checkbox v-model:checked="model[field]">(勾选此项,将在四维看看官网置顶)</Checkbox>
-          </div>
-        </template>
       </BasicForm>
     </div>
   </BasicModal>
@@ -26,15 +23,14 @@
   import { defineComponent, ref, nextTick, onMounted, reactive } from 'vue';
   import { BasicModal, useModalInner } from '/@/components/Modal';
   import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
-  import { Checkbox  } from 'ant-design-vue';
   import { useMessage } from '/@/hooks/web/useMessage';
+  import { uploadApi, tipUpdata } from '/@/api/product/index';
   import { useI18n } from '/@/hooks/web/useI18n';
-  import { uploadApi, SpaceSdkUpdate } from '/@/api/product/index';
+  import dayjs from 'dayjs';
   import { isEmojiCharacter } from '/@/utils';
-
   const { t } = useI18n();
   export default defineComponent({
-    components: { BasicModal, BasicForm, Checkbox },
+    components: { BasicModal, BasicForm },
     props: {
       userData: { type: Object },
     },
@@ -43,50 +39,81 @@
       const modelRef = ref({});
       const loading = ref(false)
       const fileFlow = reactive({
-        file:null
+        type:null
       })
       const { createMessage } = useMessage();
       const schemas: FormSchema[] = [
         {
-          field: 'fileUrl',
-          component: 'Input',
-          label: t('routes.product.types'),
-          show: false,
-          colProps: {
-            span: 24,
-          },
-        },{
           field: 'id',
           component: 'Input',
-          label: t('routes.product.types'),
+          label: 'id',
           show: false,
-          colProps: {
-            span: 24,
-          },
-        },{
-          field: 'platformType',
+        },
+        {
+          field: 'version',
           component: 'Input',
-          label: t('routes.product.types'),
-          show: false,
+          label: '版本号',
+          required: true,
+          componentProps: {
+            maxLength: 15,
+          },
           colProps: {
-            span: 24,
+            span: 20,
           },
         },{
-          field: 'type',
+          field: 'timeList',
+          label: '提示时段',
+          component: 'RangePicker',
+          required: true,
+          componentProps: {
+            format: 'YYYY-MM-DD',
+            valueFormat:'YYYY-MM-DD',
+            width:100,
+          },
+          colProps: {            
+            span: 20,
+          },
+        },
+        {
+          field: 'title',
           component: 'Input',
-          label: t('routes.product.types'),
-          slot: 'text',
+          label: '中文标题',
+          required: true,
           colProps: {
-            span: 24,
+            span: 20,
+          },
+          rules: [
+            {
+              required: true,
+              // @ts-ignore
+              validator: async (rule, value) => {
+                if (!value?.trim()) {
+                  return Promise.reject(t('common.inputText')+'中文标题');
+                }
+                if(/.*[\u4e00-\u9fa5]+.*$/.test(value)){
+                  /* eslint-disable-next-line */
+                  return Promise.reject('不支持中文字符');
+                }
+                if(isEmojiCharacter(value)){
+                  /* eslint-disable-next-line */
+                  return Promise.reject('不支持emoji表情');
+                }
+                return Promise.resolve();
+              },
+              trigger: 'change',
+            },
+          ],
+          componentProps: {
+            maxLength: 50,
           },
         },
         {
-          field: 'version',
+          field: 'titleEn',
           component: 'Input',
-          label: t('routes.product.version'),
+          label: '英文标题',
           required: true,
           colProps: {
-            span: 24,
+            span: 20,
           },
           rules: [
             {
@@ -94,7 +121,7 @@
               // @ts-ignore
               validator: async (rule, value) => {
                 if (!value?.trim()) {
-                  return Promise.reject(t('common.inputText')+t('routes.product.version'));
+                  return Promise.reject(t('common.inputText')+'英文标题');
                 }
                 if(/.*[\u4e00-\u9fa5]+.*$/.test(value)){
                   /* eslint-disable-next-line */
@@ -110,49 +137,117 @@
             },
           ],
           componentProps: {
-            disabled:true,
-            maxLength: 15,
-            onChange: (data) => {
-              console.log('data', data);
-            },
+            maxLength: 50,
           },
         },
         {
-          field: 'imprintCh',
+          field: 'description',
           component: 'InputTextArea',
-          // required: true,
+          required: true,
           label: t('routes.product.description_zh'),
           componentProps: {
             rows:4,
+            maxLength:500,
           },
           colProps: {
-            span: 24,
+            span: 20,
           },
         },
         {
-          field: 'imprintEn',
+          field: 'descriptionEn',
           component: 'InputTextArea',
-          // required: true,
+          required: true,
           label: t('routes.product.description_en'),
           componentProps: {
             rows:4,
+            maxLength:500,
           },
           colProps: {
-            span: 24,
+            span: 20,
           },
         },
         {
-          field: 'isTop',
-          component: 'CheckboxGroup',
-          label: '官网置顶',
-          slot:'Checkbox',
+          field: 'imageUrl',
+          component: 'Upload',
+          label: '中文图片/视频',
+          // helpMessage: t('routes.corporation.uploadHelp'),
+          itemProps: {
+            validateTrigger: 'onBlur',
+          },
           componentProps: {
-            options: [
-              { label: '(勾选此项,将在四维看看官网置顶)', value: 1 },
-            ],
+            api: uploadApi,
+            maxNumber: 1,
+            maxSize: 1000,
+            accept: ['jpeg','jpg','png','mp4'],
+          },
+
+          colProps: {
+            span: 20,
+          },
+        },
+        {
+          field: 'imageUrlEn',
+          component: 'Upload',
+          label: '英文图片/视频',
+          // helpMessage: t('routes.corporation.uploadHelp'),
+          itemProps: {
+            validateTrigger: 'onBlur',
+          },
+          componentProps: {
+            api: uploadApi,
+            maxNumber: 1,
+            maxSize: 1000,
+            accept: ['jpeg','jpg','png','mp4'],
+          },
+
+          colProps: {
+            span: 20,
+          },
+        },
+        {
+          field: 'infoUrl',
+          component: 'Input',
+          required: true,
+          label: '中文详情链接',
+          rules: [
+            {
+              required: true,
+              // @ts-ignore
+              validator: async (rule, value) => {
+                var reg=/(http|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:/~\+#\u4e00-\u9fa5]*[\w\-\@?^=%&/~\+#])/;
+                if (!reg.test(value)) {
+                  return Promise.reject(t('common.inputText')+'正确的地址');
+                }
+                return Promise.resolve();
+              },
+              trigger: 'change',
+            },
+          ],
+          colProps: {
+            span: 20,
           },
+        },
+        {
+          field: 'infoUrlEn',
+          component: 'Input',
+          required: true,
+          label: '英文详情链接',
+          rules: [
+            {
+              required: true,
+              // @ts-ignore
+              validator: async (rule, value) => {
+                var reg=/(http|https):\/\/[\w]+(.[\w]+)([\w\-\.,@?^=%&:/~\+#\u4e00-\u9fa5]*[\w\-\@?^=%&/~\+#])/;
+                if (!reg.test(value)) {
+                  return Promise.reject(t('common.inputText')+'正确的地址');
+                }
+                return Promise.resolve();
+              },
+              trigger: 'change',
+            },
+          ],
           colProps: {
-            span: 24,
+            span: 20,
           },
         },
       ];
@@ -167,7 +262,6 @@
       onMounted(() => {});
       let addListFunc = () => {};
       const [register, { closeModal }] = useModalInner((data) => {
-        console.log('useModalInner',data)
         data && onDataReceive(data);
       });
       function renderOwnTypeLabel(type: number): string {
@@ -199,27 +293,28 @@
         }
       }
       function onDataReceive(data) {
-        modelRef.value = data
-        let setData = {
-          ...data,
-          type:renderOwnTypeLabel(Number(data.type)),
-        }
-        console.log('onDataReceive',data,setData)
+        fileFlow.type = data.type
+        console.log('onDataReceive',data)
         resetFields();
-        setFieldsValue(setData);
+        setFieldsValue({
+          ...data,
+          imageUrl:data.imageUrl?[data.imageUrl]:[],
+          imageUrlEn:data.imageUrlEn?[data.imageUrlEn]:[],
+        });
       }
       const handleSubmit = async () => {
         loading.value = true
         try {
           const params = await validate();
-          console.log('modelRef',params)
           const apiData = {
-            // ...modelRef.value,
-            ...params as any,
-            isTop:params.isTop?1:0
+            ...params,
+            type:fileFlow.type,
+            timeList:params.timeList.map(ele => dayjs(ele).format('YYYY-MM-DD')),
+            imageUrl:params.imageUrl?.[0],
+            imageUrlEn:params.imageUrlEn?.[0],
           }
           console.log('res', apiData);
-          const res = await SpaceSdkUpdate(apiData);
+          const res = await tipUpdata(apiData);
           console.log('res', res);
           closeModal();
           resetFields();
@@ -244,11 +339,16 @@
         fileFlow,
         handleVisibleChange,
         handleSubmit,
+        loading,
         addListFunc,
         resetFields,
-        loading,
         t,
       };
     },
   });
 </script>
+<style lang="less">
+.ant-calendar-picker-input.ant-input{
+  width: 300px;
+}
+</style>

+ 27 - 36
src/views/product/updataTips/index.vue

@@ -1,7 +1,7 @@
 <template>
   <PageWrapper contentBackground>
     <template #footer>
-      <a-tabs v-model:activeKey="searchInfo.platformType" @change="tabChange">
+      <a-tabs v-model:activeKey="searchInfo.type" @change="tabChange">
         <a-tab-pane key="1" tab="四维看看" />
         <a-tab-pane key="2" tab="四维深时" />
         <a-tab-pane key="3" tab="四维全景" />
@@ -16,7 +16,7 @@
             v-if="getTypeCheckPerm('sdk-add')"
             @click="
               () => {
-                openAddModal(true, searchInfo.platformType);
+                openAddModal(true, searchInfo.type);
               }
             "
             >新增</a-button
@@ -52,8 +52,8 @@ import { Time } from '/@/components/Time';
 import { BasicTable, useTable, FormProps, TableAction, BasicColumn } from '/@/components/Table';
 import { PageWrapper } from '/@/components/Page';
 import { Divider, Card, Empty, Descriptions, Steps, Tabs,Switch } from 'ant-design-vue';
-import { SpaceSdkList, SpaceSdkDelete, SpaceSdkOnline } from '/@/api/product';
-import { SpaceSdkTop } from '/@/api/product/index';
+import { tipList, tipDelete, SpaceSdkOnline } from '/@/api/product';
+import { tipUpdata } from '/@/api/product/index';
 import { useModal } from '/@/components/Modal';
 import { useI18n } from '/@/hooks/web/useI18n';
 import AddModal from './AddModal.vue';
@@ -82,7 +82,7 @@ export default defineComponent({
     const permissionStore = usePermissionStore();
     const { getCheckPerm } = permissionStore;
     const searchInfo = reactive<Recordable>({
-      platformType: '1',
+      type: '1',
     });
     const [registerAddModal, { openModal: openAddModal }] = useModal();
     const [registerEditModal, { openModal: openEditModal }] = useModal();
@@ -111,27 +111,17 @@ export default defineComponent({
         dataIndex: 'version',
       },
       {
-        title: '版本更新说明',
+        title: '提示时段',
         width: 240,
-        dataIndex: 'imprintCh',
-      },
-      {
-        title: '首次发布时间',
-        width: 120,
-        dataIndex: 'publishTime',
+        dataIndex: 'timeList',
         customRender: ({ record }) => {
-          return (
-            record.publishTime &&
-            h(Time, {
-              value: record.publishTime,
-              mode: 'datetime',
-            })
-          );
+          return record.timeList[0] + '~' +record.timeList[1]
         },
-      },{
-        title: '创建人',
+      },
+      {
+        title: '标题',
         width: 120,
-        dataIndex: 'createName',
+        dataIndex: 'title',
       },{
         title: '创建时间',
         width: 120,
@@ -146,25 +136,26 @@ export default defineComponent({
           );
         },
       },{
-        title: '置顶',
-        dataIndex: 'isTop',
+        title: '状态',
+        dataIndex: 'banStatus',
+        // ifShow: getCheckPerm('tips-status'),
         ifShow: getCheckPerm('sdk-top'),
         width: 80,
         customRender: ({ record }) => {
-          if (!Reflect.has(record, 'isTop')) {
+          if (!Reflect.has(record, 'banStatus')) {
             record.pendingStatus = false;
           }
           return h(Switch, {
-            checked: record.isTop == 1 ? true : false,
-            checkedChildren: '',
-            unCheckedChildren: '',
-            disabled:record.status != 1,
+            checked: record.banStatus == 0 ? true : false,
+            checkedChildren: '启用',
+            unCheckedChildren: '禁用',
+            // disabled:record.status != 1,
             loading: false,
             onChange: async (checked: boolean) => {
               record.pendingStatus = true;
-              const newStatus = checked?1:0;
-              await SpaceSdkTop({...record,isTop:newStatus});
-              Reflect.set(record, 'isTop', checked);
+              const newStatus = checked?0:1;
+              await tipUpdata({...record,banStatus:newStatus});
+              Reflect.set(record, 'banStatus', checked);
               createMessage.success(t('common.optSuccess'));
               reload()
             },
@@ -180,7 +171,7 @@ export default defineComponent({
       },
     ];
     const [registerTimeTable, { reload }] = useTable({
-      api: SpaceSdkList,
+      api: tipList,
       title: '',
       columns: sdkTableSchema,
       useSearchForm: true,
@@ -223,7 +214,7 @@ export default defineComponent({
         title: () => h('span', t('sys.app.logoutTip')),
         content: () => h('span', '确定要删除吗?'),
         onOk: async () => {
-          await SpaceSdkDelete({ id: record.id });
+          await tipDelete({ id: record.id });
           createMessage.success(t('common.optSuccess'));
           reload();
         },
@@ -260,12 +251,12 @@ export default defineComponent({
       console.log('record', record);
       openEditModal(true, {
         ...record,
-        type:searchInfo.platformType
+        type:searchInfo.type
       });
     }
     
     function getTypeCheckPerm(val){
-        let myType = searchInfo.platformType
+        let myType = searchInfo.type
         return getCheckPerm(val) || getCheckPerm(`${val}-${myType}`)
       }
     return {