From 33ac908ef9692b52a1eb085b41106aae7d589ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=98=B1=E8=BE=BE?= Date: Thu, 13 Mar 2025 16:49:19 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(Design):=20=E6=96=B0=E5=A2=9E=E7=9F=A9?= =?UTF-8?q?=E9=98=B5=E9=A2=98=E5=9E=8B=E5=92=8C=E6=96=87=E4=BB=B6=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E9=A2=98=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增矩阵题型组件和相关配置 - 新增文件上传题型组件和相关配置 -优化签名题型组件 - 添新的 CSS 样式类 --- components.d.ts | 3 +- src/assets/css/main.scss | 4 + src/components/YLCascader.vue | 57 ++++++ src/components/YLPicker.vue | 125 ++++++------ src/components/contenteditable.vue | 3 +- src/request/axios/index.ts | 10 +- src/stores/modules/common.ts | 1 - src/utils/QuestionJsons/MartrixQuestion.js | 76 ++++---- src/utils/request.js | 6 +- src/utils/utils.js | 9 +- src/views/Design/Index.vue | 50 ++++- .../ActionCompoents/QuestionAction.vue | 18 +- .../QuestionItemAction/Api/Index.js | 8 + .../FieldUploadQuestionAction.vue | 182 ++++++++++++++++++ .../MartrixQuestionAction.vue | 72 +++++++ .../QuestionItemAction/QuestionBefore.vue | 7 +- .../Design/components/Questions/Choice.vue | 2 +- .../components/Questions/FileUpload.vue | 107 +++++----- .../components/Questions/MartrixQuestion.vue | 89 +++++---- .../components/Questions/MatrixCheckbox.vue | 3 +- .../components/Questions/MatrixQuestion.vue | 31 +-- .../components/Questions/SignQuestion.vue | 70 +++++-- 22 files changed, 711 insertions(+), 222 deletions(-) create mode 100644 src/components/YLCascader.vue create mode 100644 src/views/Design/components/ActionCompoents/components/QuestionItemAction/Api/Index.js create mode 100644 src/views/Design/components/ActionCompoents/components/QuestionItemAction/FieldUploadQuestionAction.vue create mode 100644 src/views/Design/components/ActionCompoents/components/QuestionItemAction/MartrixQuestionAction.vue diff --git a/components.d.ts b/components.d.ts index 39ef1c0..2162b4d 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,7 +11,6 @@ declare module 'vue' { RichText: typeof import('./src/components/RichText.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] - 'Van-': typeof import('vant/es')['-'] VanActionSheet: typeof import('vant/es')['ActionSheet'] VanButton: typeof import('vant/es')['Button'] VanCell: typeof import('vant/es')['Cell'] @@ -20,6 +19,7 @@ declare module 'vue' { VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup'] VanCol: typeof import('vant/es')['Col'] VanDivider: typeof import('vant/es')['Divider'] + VanFeild: typeof import('vant/es')['Feild'] VanField: typeof import('vant/es')['Field'] VanGrid: typeof import('vant/es')['Grid'] VanGridItem: typeof import('vant/es')['GridItem'] @@ -35,6 +35,7 @@ declare module 'vue' { VanSwitch: typeof import('vant/es')['Switch'] VanTabbar: typeof import('vant/es')['Tabbar'] VanTabbarItem: typeof import('vant/es')['TabbarItem'] + YLCascader: typeof import('./src/components/YLCascader.vue')['default'] YLPicker: typeof import('./src/components/YLPicker.vue')['default'] YLSelect: typeof import('./src/components/YLSelect.vue')['default'] } diff --git a/src/assets/css/main.scss b/src/assets/css/main.scss index 603da52..5725a99 100644 --- a/src/assets/css/main.scss +++ b/src/assets/css/main.scss @@ -11,6 +11,10 @@ a, transition: 0.4s; } +.ml10 { + margin-left: 10px; +} + @media (hover: hover) { a:hover { background-color: hsla(160deg, 100%, 37%, 0.2); diff --git a/src/components/YLCascader.vue b/src/components/YLCascader.vue new file mode 100644 index 0000000..3940ff5 --- /dev/null +++ b/src/components/YLCascader.vue @@ -0,0 +1,57 @@ + + + + diff --git a/src/components/YLPicker.vue b/src/components/YLPicker.vue index 1e98e52..7e5cf8f 100644 --- a/src/components/YLPicker.vue +++ b/src/components/YLPicker.vue @@ -84,7 +84,9 @@ const getType = (v: any) => { return Object.prototype.toString.call(v).slice(8, -1).toLowerCase(); }; // 返回指定格式的日期时间 -const getDateByFormat = (date: Date | string, fmt: dateFormat) => { +const getDateByFormat = (dateParam: Date | string, fmt: dateFormat) => { + // 避免直接修改参数 + let date = dateParam; const thisDateType = getType(date); if (date === '' || (thisDateType !== 'date' && thisDateType !== 'string')) { date = new Date(); @@ -100,7 +102,6 @@ const getDateByFormat = (date: Date | string, fmt: dateFormat) => { const m = date.getMinutes(); const s = date.getSeconds(); // 个位数补0 - // 月份比实际获取的少1,所以要加1 const _M = M < 10 ? `0${M}` : M.toString(); const _D = D < 10 ? `0${D}` : D.toString(); const _h = h < 10 ? `0${h}` : h.toString(); @@ -116,13 +117,15 @@ const getDateByFormat = (date: Date | string, fmt: dateFormat) => { }; // 比较两个日期大小 -const dateRangeLegal = (sDate: string | Date, eDate: string | Date) => { +const dateRangeLegal = (sDateParam: string | Date, eDateParam: string | Date) => { + // 避免直接修改参数 + let sDate = sDateParam; + let eDate = eDateParam; if (!sDate || !eDate) { return false; } // 补全模板 const complateStr = '0000-01-01 00:00:00'; - // 兼容ios if (typeof sDate === 'string') { sDate = (sDate + complateStr.slice(sDate.length)).replace(/-/g, '/'); } @@ -167,8 +170,8 @@ const getMaxDateLimit = computed(() => { props.format ); const tempStr = '0000-12-31 23:59:59'; - const result - = props.maxDate.length !== 0 && thisMax.length > props.maxDate.length + const result = + props.maxDate.length !== 0 && thisMax.length > props.maxDate.length ? thisMax.slice(0, props.maxDate.length) + tempStr.slice(props.maxDate.length) : thisMax; return result.slice(0, props.format.length); @@ -191,8 +194,8 @@ function onChange({ selectedValues, columnIndex }) { renderMinuteColumns, renderSecondColumns ]; - updateColumns[columnIndex] - && updateColumns[columnIndex](changeValue, getMinDateLimit.value, getMaxDateLimit.value, false); + updateColumns[columnIndex] && + updateColumns[columnIndex](changeValue, getMinDateLimit.value, getMaxDateLimit.value, false); } // 渲染全部列 @@ -318,15 +321,16 @@ const renderMonthColumns = ( s: string, e: string, isFirst: boolean, - outRange: boolean = false + outRangeParam: boolean = false ) => { - const thisY = Number(v.slice(0, 4)); // 获取当前月 - const thisM = Number(v.slice(5, 7)); // 获取当前月 - const minY = Number(s.slice(0, 4)); // 最小年份 - const maxY = Number(e.slice(0, 4)); // 最大年份 - const listArr: any = []; // 获取月份数组 - let forStart = -1; // 最小月份 - let forEnd = -1; // 最小月份 + let outRange = outRangeParam; + const thisY = Number(v.slice(0, 4)); + const thisM = Number(v.slice(5, 7)); + const minY = Number(s.slice(0, 4)); + const maxY = Number(e.slice(0, 4)); + const listArr: any = []; + let forStart = -1; + let forEnd = -1; if (thisY === minY && thisY === maxY) { forStart = Number(s.slice(5, 7)); forEnd = Number(e.slice(5, 7)); @@ -389,24 +393,25 @@ const renderDayColumns = ( s: string, e: string, isFirst: boolean, - outRange: boolean = false + outRangeParam: boolean = false ) => { - const thisYM = v.slice(0, 7); // 获取当前年月 - const thisD = Number(v.slice(8, 10)); // 获取当前日 - const startYM = s.slice(0, 7); // 开始时间临界值 - const endYM = e.slice(0, 7); // 结束时间临界值 - const listArr: any = []; // 获取月份数组 - let forStart = -1; // 最小月份 - let forEnd = -1; // 最小月份 + let outRange = outRangeParam; + const thisYM = v.slice(0, 7); + const thisD = Number(v.slice(8, 10)); + const startYM = s.slice(0, 7); + const endYM = e.slice(0, 7); + const listArr: any = []; + let forStart = -1; + let forEnd = -1; if (thisYM === startYM && thisYM === endYM) { - forStart = Number(s.slice(8, 10)); // 开始时间的天临界值 - forEnd = Number(e.slice(8, 10)); // 结束时间的天临界值 + forStart = Number(s.slice(8, 10)); + forEnd = Number(e.slice(8, 10)); } else if (thisYM === startYM) { forStart = Number(s.slice(8, 10)); forEnd = getCountDays(Number(v.slice(0, 4)), Number(v.slice(5, 7))); } else if (thisYM === endYM) { forStart = 1; - forEnd = Number(e.slice(8, 10)); // 结束时间的天临界值 + forEnd = Number(e.slice(8, 10)); } else { forStart = 1; forEnd = getCountDays(Number(v.slice(0, 4)), Number(v.slice(5, 7))); @@ -460,18 +465,24 @@ const renderHourColumns = ( s: string, e: string, isFirst: boolean, - outRange: boolean = false + outRangeParam: boolean = false ) => { - const thisYMD = v.slice(0, 10); // 获取当前年月日 - const startYMD = s.slice(0, 10); // 开始时间临界值 - const endYMD = e.slice(0, 10); // 结束时间临界值 - const thisH = Number(v.slice(11, 13)); // 获取当前小时 - const listArr: any = []; // 获取小时数组 - let forStart = -1; // 最小月份 - let forEnd = -1; // 最小月份 + // 避免直接修改参数 + let outRange = outRangeParam; + // 获取当前年月日 + const thisYMD = v.slice(0, 10); + // 开始时间临界值 + const startYMD = s.slice(0, 10); + // 结束时间临界值 + const endYMD = e.slice(0, 10); + // 获取当前小时 + const thisH = Number(v.slice(11, 13)); + const listArr: any = []; + let forStart = -1; + let forEnd = -1; if (thisYMD === startYMD && thisYMD === endYMD) { - forStart = Number(s.slice(11, 13)); // 开始时间的小时临界值 - forEnd = Number(e.slice(11, 13)); // 结束时间的小时临界值 + forStart = Number(s.slice(11, 13)); + forEnd = Number(e.slice(11, 13)); } else if (thisYMD === startYMD) { forStart = Number(s.slice(11, 13)); forEnd = 23; @@ -531,15 +542,20 @@ const renderMinuteColumns = ( s: string, e: string, isFirst: boolean, - outRange: boolean = false + outRangeParam: boolean = false ) => { - const thisYMDH = v.slice(0, 13); // 获取当前年月日小时 - const startYMDH = s.slice(0, 13); // 开始时间临界值 - const endYMDH = e.slice(0, 13); // 结束时间临界值 - const thisM = Number(v.slice(14, 16)); // 获取当前分钟 - const listArr: any = []; // 获取数组 - let forStart = -1; // 循环最小值 - let forEnd = -1; // 循环最大值 + // 避免直接修改参数 + let outRange = outRangeParam; + // 获取当前年月日小时 + const thisYMDH = v.slice(0, 13); + // 开始时间临界值 + const startYMDH = s.slice(0, 13); + // 结束时间临界值 + const endYMDH = e.slice(0, 13); + const thisM = Number(v.slice(14, 16)); + const listArr: any = []; + let forStart = -1; + let forEnd = -1; if (thisYMDH === startYMDH && thisYMDH === endYMDH) { forStart = Number(s.slice(14, 16)); forEnd = Number(e.slice(14, 16)); @@ -602,15 +618,16 @@ const renderSecondColumns = ( s: string, e: string, isFirst: boolean, - outRange: boolean = false + outRangeParam: boolean = false ) => { - const thisYMDHM = v.slice(0, 16); // 获取当前年月日小时 - const startYMDHM = s.slice(0, 16); // 开始时间临界值 - const endYMDHM = e.slice(0, 16); // 结束时间临界值 - const thisS = Number(v.slice(17, 19)); // 获取当前分钟 - const listArr: any = []; // 获取数组 - let forStart = -1; // 循环最小值 - let forEnd = -1; // 循环最大值 + let outRange = outRangeParam; + const thisYMDHM = v.slice(0, 16); + const startYMDHM = s.slice(0, 16); + const endYMDHM = e.slice(0, 16); + const thisS = Number(v.slice(17, 19)); + const listArr: any = []; + let forStart = -1; + let forEnd = -1; if (thisYMDHM === startYMDHM && thisYMDHM === endYMDHM) { forStart = Number(s.slice(17, 19)); forEnd = Number(e.slice(17, 19)); @@ -665,7 +682,7 @@ watch( } }, { - immediate: true // 立即监听--进入就会执行一次 监听显影状态 + immediate: true } ); diff --git a/src/components/contenteditable.vue b/src/components/contenteditable.vue index d49ad9d..c380353 100644 --- a/src/components/contenteditable.vue +++ b/src/components/contenteditable.vue @@ -88,7 +88,6 @@ const checkContains = (element, target) => { try { return element?.contains(target) ?? false; } catch (e) { - console.error('Contains check failed:', e); return false; } }; @@ -134,11 +133,11 @@ onMounted(() => { bottom: 0; left: 0; z-index: 2008; + display: flex; width: 100%; height: 40px; padding: 0 10px; background: #fff; - display: flex; line-height: 40px; & button { diff --git a/src/request/axios/index.ts b/src/request/axios/index.ts index 3ec1964..8d7e34c 100644 --- a/src/request/axios/index.ts +++ b/src/request/axios/index.ts @@ -10,7 +10,8 @@ import axios from 'axios'; const service = axios.create({ // baseURL: `${baseURL}`, // url = base url + request url // withCredentials: true, // send cookies when cross-domain requests - timeout: 30000 // request timeout + // request timeout + timeout: 30000 }); // request interceptor @@ -36,7 +37,12 @@ service.interceptors.request.use( // response interceptor service.interceptors.response.use( (response) => { - if (response.status === 200 || response.status === 201 || response.status === 202 || response.status === 204) { + if ( + response.status === 200 + || response.status === 201 + || response.status === 202 + || response.status === 204 + ) { if (response.config.method === 'put') { // message.success('保存中...'); } diff --git a/src/stores/modules/common.ts b/src/stores/modules/common.ts index 298dc9d..796606f 100644 --- a/src/stores/modules/common.ts +++ b/src/stores/modules/common.ts @@ -407,7 +407,6 @@ export const useCommonStore = defineStore('common', { }), actions: { async fetchQuestionInfo(questionInfo) { - console.log(questionInfo, 456); try { if (!questionInfo) return; diff --git a/src/utils/QuestionJsons/MartrixQuestion.js b/src/utils/QuestionJsons/MartrixQuestion.js index 583eccf..19dba37 100644 --- a/src/utils/QuestionJsons/MartrixQuestion.js +++ b/src/utils/QuestionJsons/MartrixQuestion.js @@ -7,44 +7,44 @@ export default { title: '', options: [ [ - { - id: '', - is_fixed: 0, - is_other: 0, - is_remove_other: 0, - option: '

选项1

', - option_config: { - image_url: [], - title: '', - instructions: [], - option_type: 0, - limit_right_content: '' - }, - option_index: 1, - parent_id: 0, - type: 0, - cascade: [], - config: [] - }, - { - id: '', - is_fixed: 0, - is_other: 0, - is_remove_other: 0, - option: '

选项2

', - option_config: { - image_url: [], - title: '', - instructions: [], - option_type: 0, - limit_right_content: '' - }, - option_index: 1, - parent_id: 0, - type: 0, - cascade: [], - config: [] - } + // { + // id: '', + // is_fixed: 0, + // is_other: 0, + // is_remove_other: 0, + // option: '

选项1

', + // option_config: { + // image_url: [], + // title: '', + // instructions: [], + // option_type: 0, + // limit_right_content: '' + // }, + // option_index: 1, + // parent_id: 0, + // type: 0, + // cascade: [], + // config: [] + // }, + // { + // id: '', + // is_fixed: 0, + // is_other: 0, + // is_remove_other: 0, + // option: '

选项2

', + // option_config: { + // image_url: [], + // title: '', + // instructions: [], + // option_type: 0, + // limit_right_content: '' + // }, + // option_index: 1, + // parent_id: 0, + // type: 0, + // cascade: [], + // config: [] + // } ], [ { diff --git a/src/utils/request.js b/src/utils/request.js index d583f16..1e78411 100644 --- a/src/utils/request.js +++ b/src/utils/request.js @@ -12,9 +12,11 @@ const baseURL = config.default.proxyUrl; // create an axios instance const service = axios.create({ - baseURL: `${baseURL}`, // url = base url + request url + // url = base url + request url + baseURL: `${baseURL}`, // withCredentials: true, // send cookies when cross-domain requests - timeout: 30000 // request timeout + // request timeout + timeout: 30000 }); // request interceptor diff --git a/src/utils/utils.js b/src/utils/utils.js index 0521618..acecf9f 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -7,7 +7,8 @@ */ export function getSequenceName(names, list, options) { const result = []; - const start = options?.start ?? 1; // 从数字几开始 + // 从数字几开始 + const start = options?.start ?? 1; names.forEach((name) => { const sequence = []; @@ -61,7 +62,8 @@ export function getDomText(dom) { * @returns {boolean[]} */ export function isRichRepeat(list, comparedList, options) { - const imageAsDifference = options?.imageAsDifference !== false; // 不比较图片。只要存在图片,则认为两段富文本不同 + // 不比较图片。只要存在图片,则认为两段富文本不同 + const imageAsDifference = options?.imageAsDifference !== false; return list.map((rich) => { return comparedList.some((compared) => { @@ -83,7 +85,8 @@ export function isRichRepeat(list, comparedList, options) { * @returns {boolean[]} */ export function isRichEmpty(list, options) { - const imageAsEmpty = options?.imageAsEmpty === true; // 不判断图片。只要存在图片,则认为两段富文本不为空 + // 不判断图片。只要存在图片,则认为两段富文本不为空 + const imageAsEmpty = options?.imageAsEmpty === true; return list.map((rich) => { if (!imageAsEmpty) { diff --git a/src/views/Design/Index.vue b/src/views/Design/Index.vue index 4b65d24..da98914 100644 --- a/src/views/Design/Index.vue +++ b/src/views/Design/Index.vue @@ -293,7 +293,6 @@ const actionEvent = (item, el) => { const actionFun = { // 单选事件 添加选项 radioAddOption: (element) => { - console.log(element); element.options.map((item) => { item.push({ id: uuidv4(), @@ -313,6 +312,8 @@ const actionFun = { }); }); element.last_option_index += 1; + + saveQueItem(questionInfo.value.logics, [element]); }, /** @@ -320,16 +321,59 @@ const actionFun = { * @param element {import('./components/Questions/types/martrix.js').MatrixSurveyQuestion} */ addMatrixRowOption: (element) => { + const optionIndex = element.last_option_index; element.options[0].push({ - option: '新增行' + cascade: [], + config: [], + is_fixed: 0, + is_other: 0, + is_remove_other: 0, + option: `

行标签${element.options[0].length + 1}

`, + option_config: { + image_url: [], + title: '', + instructions: [], + option_type: 0, + limit_right_content: '

右极文字1

' + }, + parent_id: 0, + type: 1, + id: uuidv4(), + option_index: optionIndex + 1 }); + + element.last_option_index = optionIndex + 1; + + saveQueItem(questionInfo.value.logics, [element]); }, /** * martrix 矩阵列数增加 * @param element {import('./components/Questions/types/martrix.js').MatrixSurveyQuestion} */ addMatrixColumnOption: (element) => { - element.options[1].push({ option: '新增列' }); + const optionIndex = element.last_option_index; + element.options[1].push({ + cascade: [], + config: [], + is_fixed: 0, + is_other: 0, + is_remove_other: 0, + option: `

列标签${element.options[1].length + 1}

`, + option_config: { + image_url: [], + title: '', + instructions: [], + option_type: 0, + limit_right_content: '

右极文字1

' + }, + parent_id: 0, + type: 2, + id: uuidv4(), + option_index: optionIndex + 1 + }); + + element.last_option_index = optionIndex + 1; + saveQueItem(questionInfo.value.logics, [element]); } }; diff --git a/src/views/Design/components/ActionCompoents/QuestionAction.vue b/src/views/Design/components/ActionCompoents/QuestionAction.vue index 442c7a1..7d90e9e 100644 --- a/src/views/Design/components/ActionCompoents/QuestionAction.vue +++ b/src/views/Design/components/ActionCompoents/QuestionAction.vue @@ -29,7 +29,7 @@ @@ -44,6 +44,13 @@ > + + +