Merge branch 'feature/feature-20250331-h5' of https://e.coding.yili.com/yldc/ylst/ylst-survey-h5 into feature/feature-20250331-h5

This commit is contained in:
Huangzhe
2025-03-11 18:29:45 +08:00
42 changed files with 2891 additions and 496 deletions

10
.env Normal file
View File

@@ -0,0 +1,10 @@
# .env
VITE_APP_BASE_URL=http://192.168.8.165:15011/
VITE_APP_ENV=development
VITE_APP_CURRENTMODE=dev
VITE_APP_BASEOSS=https://diaoyan-files.automark.cc
VITE_APP_DELIVERY_BASEURL=https://javaxq.test.automark.cc/
VITE_APP_MESSAGE_CENTER=http://gtech-gateway.dcin-test.digitalyili.com/apigtech/message-send-center/
VITE_APP_SOCKETURL=wss://yls-api-uat.dctest.digitalyili.com/survey_sync
VITE_APP_JSONPURL=https://iam-uat.dctest.digitalyili.com/idp/restful/getIDPToken
VITE_APP_YQRURL=https://ocp-uat-ain.digitalyili.com

View File

@@ -1,2 +1,10 @@
VITE_APP_BASE_URL = '/'
VITE_APP_ENV = 'development'
# .env.development
VITE_APP_BASEURL=http://192.168.8.165:15011/
VITE_APP_ENV=development
VITE_APP_CURRENTMODE=dev
VITE_APP_BASEOSS=https://diaoyan-files.automark.cc
VITE_APP_DELIVERY_BASEURL=https://javaxq.test.automark.cc/
VITE_APP_MESSAGE_CENTER=http://gtech-gateway.dcin-test.digitalyili.com/apigtech/message-send-center/
VITE_APP_SOCKETURL=wss://yls-api-uat.dctest.digitalyili.com/survey_sync
VITE_APP_JSONPURL=https://iam-uat.dctest.digitalyili.com/idp/restful/getIDPToken
VITE_APP_YQRURL=https://ocp-uat-ain.digitalyili.com

View File

@@ -1,2 +0,0 @@
VITE_APP_BASE_URL = 'https://xx.digitalyili.com'
VITE_APP_ENV = 'prod'

View File

@@ -1,2 +0,0 @@
VITE_APP_BASE_URL = 'https://sit.xx.digitalyili.com'
VITE_APP_ENV = 'sit'

View File

@@ -1,2 +0,0 @@
VITE_APP_BASE_URL = 'https://uat.xx.digitalyili.com'
VITE_APP_ENV = 'uat'

4
auto-imports.d.ts vendored
View File

@@ -5,4 +5,6 @@
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {}
declare global {
}

9
components.d.ts vendored
View File

@@ -19,11 +19,18 @@ declare module 'vue' {
VanCheckbox: typeof import('vant/es')['Checkbox']
VanCheckboxGroup: typeof import('vant/es')['CheckboxGroup']
VanCol: typeof import('vant/es')['Col']
VanDatePicker: typeof import('vant/es')['DatePicker']
VanDatetimePicker: typeof import('vant/es')['DatetimePicker']
VanDialog: typeof import('vant/es')['Dialog']
VanDivider: typeof import('vant/es')['Divider']
VanFiel: typeof import('vant/es')['Fiel']
VanField: typeof import('vant/es')['Field']
VanGrid: typeof import('vant/es')['Grid']
VanGridItem: typeof import('vant/es')['GridItem']
VanIcon: typeof import('vant/es')['Icon']
VanNavBar: typeof import('vant/es')['NavBar']
VanPicker: typeof import('vant/es')['Picker']
VanPikcer: typeof import('vant/es')['Pikcer']
VanPopup: typeof import('vant/es')['Popup']
VanRadio: typeof import('vant/es')['Radio']
VanRadioGroup: typeof import('vant/es')['RadioGroup']
@@ -33,6 +40,8 @@ declare module 'vue' {
VanSwitch: typeof import('vant/es')['Switch']
VanTabbar: typeof import('vant/es')['Tabbar']
VanTabbarItem: typeof import('vant/es')['TabbarItem']
VanTimePicker: typeof import('vant/es')['TimePicker']
YLPicker: typeof import('./src/components/YLPicker.vue')['default']
YLSelect: typeof import('./src/components/YLSelect.vue')['default']
}
}

125
package-lock.json generated
View File

@@ -9,12 +9,15 @@
"version": "0.0.0",
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.8.2",
"dotenv": "^16.4.7",
"element-plus": "^2.7.8",
"lodash": "^4.17.21",
"pinia": "^2.1.7",
"sortablejs": "^1.15.6",
"uuid": "^11.1.0",
"vant": "^4.9.17",
"vite-plugin-vue": "^0.0.1",
"vue": "^3.4.29",
"vue-router": "^4.3.3",
"vuex": "^4.1.0"
@@ -4782,6 +4785,11 @@
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@@ -4797,6 +4805,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/axios": {
"version": "1.8.2",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.8.2.tgz",
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -4974,7 +4992,6 @@
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -5100,6 +5117,17 @@
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz",
@@ -5311,6 +5339,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-1.0.3.tgz",
@@ -5404,9 +5440,19 @@
"url": "https://github.com/fb55/domutils?sponsor=1"
}
},
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
@@ -5548,7 +5594,6 @@
},
"node_modules/es-define-property": {
"version": "1.0.1",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -5556,7 +5601,6 @@
},
"node_modules/es-errors": {
"version": "1.3.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -5591,7 +5635,6 @@
},
"node_modules/es-object-atoms": {
"version": "1.1.1",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -5602,7 +5645,6 @@
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"dev": true,
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
@@ -6614,6 +6656,25 @@
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": {
"version": "0.3.5",
"resolved": "https://registry.npmmirror.com/for-each/-/for-each-0.3.5.tgz",
@@ -6629,6 +6690,20 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.2.tgz",
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz",
@@ -6652,7 +6727,6 @@
},
"node_modules/function-bind": {
"version": "1.1.2",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -6697,7 +6771,6 @@
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"dev": true,
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
@@ -6720,7 +6793,6 @@
},
"node_modules/get-proto": {
"version": "1.0.1",
"dev": true,
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
@@ -6914,7 +6986,6 @@
},
"node_modules/gopd": {
"version": "1.2.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -6978,7 +7049,6 @@
},
"node_modules/has-symbols": {
"version": "1.1.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -6989,7 +7059,6 @@
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
@@ -7003,7 +7072,6 @@
},
"node_modules/hasown": {
"version": "2.0.2",
"dev": true,
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
@@ -7826,7 +7894,6 @@
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -7908,6 +7975,25 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": {
"version": "9.0.3",
"resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.3.tgz",
@@ -8662,6 +8748,11 @@
"react-is": "^16.13.1"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
@@ -10450,6 +10541,12 @@
}
}
},
"node_modules/vite-plugin-vue": {
"version": "0.0.1",
"resolved": "https://registry.npmmirror.com/vite-plugin-vue/-/vite-plugin-vue-0.0.1.tgz",
"integrity": "sha512-PhAy+v+t6W3JAeqFxf6w/hGyURZ2qvrJMS/HTeCPLnfllz+O5wNP49TcAb4MROeif93kKIOigtDFJowaLuU+0w==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."
},
"node_modules/vscode-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz",

View File

@@ -4,7 +4,7 @@
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"dev": "vite --mode development",
"build": "run-p type-check \"build-only {@}\" --",
"preview": "vite preview",
"build-only": "vite build",
@@ -15,13 +15,14 @@
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.8.2",
"dotenv": "^16.4.7",
"element-plus": "^2.7.8",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"pinia": "^2.1.7",
"sortablejs": "^1.15.6",
"uuid": "^11.1.0",
"vant": "^4.9.17",
"vite-plugin-vue": "^0.0.1",
"vue": "^3.4.29",
"vue-router": "^4.3.3",
"vuex": "^4.1.0"

31
src/api/design/index.js Normal file
View File

@@ -0,0 +1,31 @@
import request from '@/utils/request.js';
export function snQuestions(params) {
return request({
url: `/console/surveys/${params.sn}/questions`,
method: 'get'
});
}
// 操作编辑
export function saveQuestion(params) {
return request({
url: `console/surveys/${params.sn}/questions`,
method: 'post',
data: params
});
}
export function sync(params) {
return request({
url: `console/surveys/${params.sn}/sync`,
method: 'post',
data: params
});
}
export function questionDetails(params) {
// let sn = params.sn;
// delete params.sn;
return request({
url: `console/surveys/${params.sn}/details`,
method: 'post',
data: params
});
}

19
src/api/home/index.js Normal file
View File

@@ -0,0 +1,19 @@
import request from '@/utils/request.js';
export function getQuestionList(params) {
return request({
url: '/console/template/list_shortcut',
method: 'get',
params
});
}
export function consoleSurveys(params) {
return request({
url: '/console/surveys',
method: 'post',
data: {
...params,
source: '1'
}
});
}

View File

@@ -1,6 +1,7 @@
@import 'base';
@import '../../fonts/iconfont.css';
@import 'vant';
@import '../../fonts/moblie/iconfont.css';
a,
.green {

689
src/components/YLPicker.vue Normal file
View File

@@ -0,0 +1,689 @@
<!-- vant的picker组件只支持年月日有些情况下需要一个能支持选择年月日时分秒的picker组件 -->
<!-- 本组件可支持年月日时分秒任意格式dateFormat -->
<template>
<!-- 弹出层 -->
<van-picker
ref="dateTimePicker"
v-model="data.selectedValues"
:title="title"
:columns="data.columns"
swipe-duration="500"
@cancel="cancelOn"
@confirm="onConfirm"
@change="onChange"
/>
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed } from 'vue';
// 所有可能用到的日期格式
type dateFormat =
| 'YYYY'
| 'YYYY-MM'
| 'YYYY-MM-DD'
| 'YYYY-MM-DD HH'
| 'YYYY-MM-DD HH:mm'
| 'YYYY-MM-DD HH:mm:ss';
/** 声明props类型 */
interface Props {
// 传入的显示隐藏状态
showPicker: boolean;
// picker标题
title?: string;
// 传入的日期值
values: string;
// 自定义格式
format?: dateFormat;
// 自定义单位
units?: string[];
// 最小日期时间
minDate?: string;
// 最大日期时间
maxDate?: string;
}
interface IntColumnsItem {
text: string;
value: string;
children?: IntColumnsItem[];
}
const props = withDefaults(defineProps<Props>(), {
showPicker: false,
title: '请选择日期',
values: '',
format: 'YYYY-MM-DD HH:mm',
units: () => [],
minDate: '',
maxDate: ''
});
// 定义要向父组件传递的事件
const emit = defineEmits(['cancel', 'confirm']);
const dateTimePicker = ref(null);
// 定义数据
const data = reactive<{
isPicker: boolean;
columns: IntColumnsItem[];
selectedValues: any[];
tempSelectedValues: any[];
}>({
// 是否显示弹出层
isPicker: false,
// 所有时间列
columns: [],
// 控件选择的时间值
selectedValues: [],
// 临时数据,数据变化过程中选中的值
tempSelectedValues: []
});
// 获取数据类型
const getType = (v: any) => {
return Object.prototype.toString.call(v).slice(8, -1).toLowerCase();
};
// 返回指定格式的日期时间
const getDateByFormat = (date: Date | string, fmt: dateFormat) => {
const thisDateType = getType(date);
if (date === '' || (thisDateType !== 'date' && thisDateType !== 'string')) {
date = new Date();
} else if (thisDateType === 'string') {
date = new Date((date as string).replace(/-/g, '/'));
} else {
date = new Date(date);
}
const Y = date.getFullYear();
const M = date.getMonth() + 1;
const D = date.getDate();
const h = date.getHours();
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();
const _m = m < 10 ? `0${m}` : m.toString();
const _s = s < 10 ? `0${s}` : s.toString();
return fmt
.replace(/YYYY/g, Y.toString())
.replace('MM', _M)
.replace('DD', _D)
.replace('HH', _h)
.replace('mm', _m)
.replace('ss', _s);
};
// 比较两个日期大小
const dateRangeLegal = (sDate: string | Date, eDate: string | Date) => {
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, '/');
}
if (typeof eDate === 'string') {
eDate = (eDate + complateStr.slice(eDate.length)).replace(/-/g, '/');
}
return new Date(sDate) <= new Date(eDate);
};
/**
* 空闲位自动补全0
* @param num 数据
* @param len 设定长度
*/
const autoFillZero = (num: number, len: number) => {
return (Array(len).join('0') + num).slice(-len);
};
// 获取某年某月有多少天
const getCountDays = (year: number, month: number) => {
// 获取某年某月多少天
const day = new Date(year, month, 0);
return day.getDate();
};
// 获取最小时间范围
const getMinDateLimit = computed(() => {
return getDateByFormat(
props.minDate && props.minDate.length >= 0
? props.minDate
: `${new Date().getFullYear() - 10}-01-01 00:00:00`,
props.format
);
});
// 获取最大时间范围
const getMaxDateLimit = computed(() => {
const thisMax = getDateByFormat(
props.maxDate && props.maxDate.length >= 0
? props.maxDate
: `${new Date().getFullYear() + 10}-12-31 23:59:59`,
props.format
);
const tempStr = '0000-12-31 23:59:59';
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);
});
function onChange({ selectedValues, columnIndex }) {
// 更新渲染变化的列之后的所有列columnIndex
const splitArr = ['-', '-', ' ', ':', ':'];
let iiindex = -1;
const changeValue = selectedValues.reduce((a, b) => {
iiindex += 1;
return a + splitArr[iiindex] + b;
});
// 当前列变化后此时此刻时间
// 更新当前列之后的所有列(包括当前列)
const updateColumns = [
renderYearColumns,
renderMonthColumns,
renderDayColumns,
renderHourColumns,
renderMinuteColumns,
renderSecondColumns
];
updateColumns[columnIndex]
&& updateColumns[columnIndex](changeValue, getMinDateLimit.value, getMaxDateLimit.value, false);
}
// 渲染全部列
const getcolumns = () => {
// 先清空全部列
data.columns = [];
// 清空年月日时分秒时间值
data.tempSelectedValues = [];
data.selectedValues = [];
// 获取props.values转换成指定格式后的日期时间
const defaultDateTime = getDateByFormat(props.values, props.format);
let usefullDateTime = defaultDateTime;
if (!dateRangeLegal(getMinDateLimit.value, defaultDateTime)) {
usefullDateTime = getMinDateLimit.value;
} else if (!dateRangeLegal(defaultDateTime, getMaxDateLimit.value)) {
usefullDateTime = getMaxDateLimit.value;
}
// 渲染修正“年”列
renderYearColumns(usefullDateTime, getMinDateLimit.value, getMaxDateLimit.value, true);
};
/**
* 渲染年所在列,并自动修正在“年”这一列列范围之外的数据
* @param v 需要做比较的数据dateFormat格式
* @param s 比较条件开始时间临界值dateFormat格式
* @param e 比较条件结束时间临界值dateFormat格式
* @param isFirst 是否是第一次渲染,需要定位到当前
* @param outRange 是否超出范围之外
*/
const renderYearColumns = (v: string, s: string, e: string, isFirst: boolean) => {
// 设置年范围
// 获取前后十年数组
const listArr: any = [];
const Currentday = new Date().getFullYear();
// 最小月份
let forStart = 2020;
// 最小月份
let forEnd = 2040;
if (s && e) {
const startYearLimit = s.slice(0, 4);
const endYearLimit = e.slice(0, 4);
// 如果最小、最大日期设定的范围错误
if (!dateRangeLegal(s, e)) {
forStart = Currentday - 10;
forEnd = Currentday + 10;
} else {
if (startYearLimit === endYearLimit) {
forStart = Number(startYearLimit);
forEnd = Number(startYearLimit);
} else {
forStart = Number(startYearLimit);
forEnd = Number(endYearLimit);
}
}
} else if (s) {
const startYearLimit = s.slice(0, 4);
if (Currentday <= Number(startYearLimit)) {
forStart = Number(startYearLimit);
forEnd = Number(startYearLimit) + 10;
} else {
forStart = Number(startYearLimit);
forEnd = Currentday + 10;
}
} else if (e) {
const endYearLimit = e.slice(0, 4);
if (Currentday <= Number(endYearLimit)) {
forStart = Currentday - 10;
forEnd = Number(endYearLimit);
} else {
forStart = Number(endYearLimit) - 10;
forEnd = Number(endYearLimit);
}
} else {
forStart = Currentday - 10;
forEnd = Currentday + 10;
}
for (let m = forStart; m <= forEnd; m++) {
listArr.push({
text: `${m}${props.units[0] || ''}`,
value: m.toString()
});
}
// 判断当前“年”是否在以上合理范围之内
const thisYear = Number(v.slice(0, 4));
// 当前定位到的年份
let vmValue: string | number = '';
if (thisYear < forStart) {
vmValue = forStart;
} else if (thisYear > forEnd) {
vmValue = forEnd;
} else {
// 范围正确
vmValue = thisYear;
}
// 插入/更新到data中
if (isFirst) {
data.columns.push(listArr);
data.tempSelectedValues.push(autoFillZero(vmValue, 4));
} else {
data.columns[0] = listArr;
data.tempSelectedValues[0] = autoFillZero(vmValue, 4);
}
if (props.format.length >= 7) {
// 至少是“YYYY-MM”格式则渲染“月”
// 根据当前年渲染“月”这一列
renderMonthColumns(autoFillZero(vmValue, 4) + v.slice(4), s, e, isFirst);
} else {
// 确定选择结果
data.selectedValues = [...data.tempSelectedValues];
}
};
/**
* 渲染月所在列,并自动修正在“月”这一列列范围之外的数据
* @param v 需要做比较的数据dateFormat格式
* @param s 比较条件开始时间临界值dateFormat格式
* @param e 比较条件结束时间临界值dateFormat格式
* @param isFirst 是否是第一次渲染,需要定位到当前
* @param outRange 是否超出范围之外
*/
const renderMonthColumns = (
v: string,
s: string,
e: string,
isFirst: boolean,
outRange: 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; // 最小月份
if (thisY === minY && thisY === maxY) {
forStart = Number(s.slice(5, 7));
forEnd = Number(e.slice(5, 7));
} else if (thisY === minY) {
forStart = Number(s.slice(5, 7));
forEnd = 12;
} else if (thisY === maxY) {
forStart = 1;
forEnd = Number(e.slice(5, 7));
} else {
forStart = 1;
forEnd = 12;
}
for (let m = forStart; m <= forEnd; m++) {
listArr.push({
text: autoFillZero(m, 2) + (props.units[1] || ''),
value: autoFillZero(m, 2)
});
}
// 判断当前月是否在此范围之内
let vmValue: string | number = '';
if (thisM < forStart || outRange) {
vmValue = forStart;
outRange = true;
} else if (thisM > forEnd) {
vmValue = forEnd;
outRange = true;
} else {
// 范围正确
vmValue = thisM;
}
// 插入/更新到data中
if (isFirst) {
data.columns.push(listArr);
data.tempSelectedValues.push(autoFillZero(vmValue, 2));
} else {
data.columns[1] = listArr;
data.tempSelectedValues[1] = autoFillZero(vmValue, 2);
}
if (props.format.length >= 10) {
// 至少是“YYYY-MM-DD”格式则渲染“日”
// 根据当前年渲染“日”这一列
renderDayColumns(v.slice(0, 5) + autoFillZero(vmValue, 2) + v.slice(7), s, e, isFirst);
} else {
// 确定选择结果
data.selectedValues = [...data.tempSelectedValues];
}
};
/**
* 渲染日所在列,并自动修正在“日”这一列列范围之外的数据
* @param v 需要做比较的数据dateFormat格式
* @param s 比较条件开始时间临界值dateFormat格式
* @param e 比较条件结束时间临界值dateFormat格式
* @param isFirst 是否是第一次渲染,需要定位到当前
* @param outRange 是否超出范围之外
*/
const renderDayColumns = (
v: string,
s: string,
e: string,
isFirst: boolean,
outRange: 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; // 最小月份
if (thisYM === startYM && thisYM === endYM) {
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)); // 结束时间的天临界值
} else {
forStart = 1;
forEnd = getCountDays(Number(v.slice(0, 4)), Number(v.slice(5, 7)));
}
for (let m = forStart; m <= forEnd; m++) {
listArr.push({
text: autoFillZero(m, 2) + (props.units[2] || ''),
value: autoFillZero(m, 2)
});
}
// 判断当前日是否在此范围之内
let vmValue: string | number = '';
if (thisD < forStart || outRange) {
vmValue = forStart;
outRange = true;
} else if (thisD > forEnd) {
vmValue = forEnd;
outRange = true;
} else {
// 范围正确
vmValue = thisD;
}
// 插入/更新到data中
if (isFirst) {
data.columns.push(listArr);
data.tempSelectedValues.push(autoFillZero(vmValue, 2));
} else {
data.columns[2] = listArr;
data.tempSelectedValues[2] = autoFillZero(vmValue, 2);
}
if (props.format.length >= 13) {
// 至少是“YYYY-MM-DD HH”格式则渲染“时”
// 根据当前年渲染“日”这一列
renderHourColumns(v.slice(0, 8) + autoFillZero(vmValue, 2) + v.slice(10), s, e, isFirst);
} else {
// 确定选择结果
data.selectedValues = [...data.tempSelectedValues];
}
};
/**
* 渲染小时所在列,并自动修正在“时”这一列列范围之外的数据
* @param v 需要做比较的数据dateFormat格式
* @param s 比较条件开始时间临界值dateFormat格式
* @param e 比较条件结束时间临界值dateFormat格式
* @param isFirst 是否是第一次渲染,需要定位到当前
* @param outRange 是否超出范围之外
*/
const renderHourColumns = (
v: string,
s: string,
e: string,
isFirst: boolean,
outRange: 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; // 最小月份
if (thisYMD === startYMD && thisYMD === endYMD) {
forStart = Number(s.slice(11, 13)); // 开始时间的小时临界值
forEnd = Number(e.slice(11, 13)); // 结束时间的小时临界值
} else if (thisYMD === startYMD) {
forStart = Number(s.slice(11, 13));
forEnd = 23;
} else if (thisYMD === endYMD) {
forStart = 0;
forEnd = Number(e.slice(11, 13));
} else {
forStart = 0;
forEnd = 23;
}
for (let m = forStart; m <= forEnd; m++) {
listArr.push({
text: autoFillZero(m, 2) + (props.units[3] || ''),
value: autoFillZero(m, 2)
});
}
// 判断当前小时是否在此范围之内
let vmValue: string | number = '';
if (thisH < forStart || outRange) {
vmValue = forStart;
outRange = true;
} else if (thisH > forEnd) {
vmValue = forEnd;
outRange = true;
} else {
// 范围正确
vmValue = thisH;
}
// 插入/更新到data中
if (isFirst) {
data.columns.push(listArr);
data.tempSelectedValues.push(autoFillZero(vmValue, 2));
} else {
data.columns[3] = listArr;
data.tempSelectedValues[3] = autoFillZero(vmValue, 2);
}
if (props.format.length >= 16) {
// 至少是“YYYY-MM-DD HH:mm”格式则渲染“分”
// 根据当前年渲染“分”这一列
renderMinuteColumns(v.slice(0, 11) + autoFillZero(vmValue, 2) + v.slice(13), s, e, isFirst);
} else {
// 确定选择结果
data.selectedValues = [...data.tempSelectedValues];
}
};
/**
* 渲染分钟所在列,并自动修正在“分”这一列列范围之外的数据
* @param v 需要做比较的数据dateFormat格式
* @param s 比较条件开始时间临界值dateFormat格式
* @param e 比较条件结束时间临界值dateFormat格式
* @param isFirst 是否是第一次渲染,需要定位到当前
* @param outRange 是否超出范围之外
*/
const renderMinuteColumns = (
v: string,
s: string,
e: string,
isFirst: boolean,
outRange: 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; // 循环最大值
if (thisYMDH === startYMDH && thisYMDH === endYMDH) {
forStart = Number(s.slice(14, 16));
forEnd = Number(e.slice(14, 16));
} else if (thisYMDH === startYMDH) {
forStart = Number(s.slice(14, 16));
forEnd = 59;
} else if (thisYMDH === endYMDH) {
forStart = 0;
forEnd = Number(e.slice(14, 16));
} else {
forStart = 0;
forEnd = 59;
}
for (let m = forStart; m <= forEnd; m++) {
listArr.push({
text: autoFillZero(m, 2) + (props.units[4] || ''),
value: autoFillZero(m, 2)
});
}
// 判断当前小时是否在此范围之内
let vmValue: string | number = '';
if (thisM < forStart || outRange) {
vmValue = forStart;
outRange = true;
} else if (thisM > forEnd) {
vmValue = forEnd;
outRange = true;
} else {
// 范围正确
vmValue = thisM;
}
// 插入/更新到data中
if (isFirst) {
data.columns.push(listArr);
data.tempSelectedValues.push(autoFillZero(vmValue, 2));
} else {
data.columns[4] = listArr;
data.tempSelectedValues[4] = autoFillZero(vmValue, 2);
}
if (props.format.length === 19) {
// 至少是“YYYY-MM-DD HH:mm:ss”格式则渲染“秒”
// 根据当前年渲染“秒”这一列
renderSecondColumns(v.slice(0, 14) + autoFillZero(vmValue, 2) + v.slice(16), s, e, isFirst);
} else {
// 确定选择结果
data.selectedValues = [...data.tempSelectedValues];
}
};
/**
* 渲染秒钟所在列,并自动修正在“秒”这一列列范围之外的数据
* @param v 需要做比较的数据dateFormat格式
* @param s 比较条件开始时间临界值dateFormat格式
* @param e 比较条件结束时间临界值dateFormat格式
* @param isFirst 是否是第一次渲染,需要定位到当前
* @param outRange 是否超出范围之外
*/
const renderSecondColumns = (
v: string,
s: string,
e: string,
isFirst: boolean,
outRange: 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; // 循环最大值
if (thisYMDHM === startYMDHM && thisYMDHM === endYMDHM) {
forStart = Number(s.slice(17, 19));
forEnd = Number(e.slice(17, 19));
} else if (thisYMDHM === startYMDHM) {
forStart = Number(s.slice(17, 19));
forEnd = 59;
} else if (thisYMDHM === endYMDHM) {
forStart = 0;
forEnd = Number(e.slice(17, 19));
} else {
forStart = 0;
forEnd = 59;
}
for (let m = forStart; m <= forEnd; m++) {
listArr.push({
text: autoFillZero(m, 2) + (props.units[5] || ''),
value: autoFillZero(m, 2)
});
}
// 判断当前小时是否在此范围之内
let vmValue: string | number = '';
if (thisS < forStart || outRange) {
vmValue = forStart;
outRange = true;
} else if (thisS > forEnd) {
vmValue = forEnd;
outRange = true;
} else {
// 范围正确
vmValue = thisS;
}
// 插入/更新到data中
if (isFirst) {
data.columns.push(listArr);
data.tempSelectedValues.push(autoFillZero(vmValue, 2));
} else {
data.columns[5] = listArr;
data.tempSelectedValues[5] = autoFillZero(vmValue, 2);
}
// 确定选择结果
data.selectedValues = [...data.tempSelectedValues];
};
watch(
() => props.showPicker,
(val) => {
data.isPicker = val;
if (val) {
// console.log("当前最大最小值判断结果", getMinDateLimit.value, getMaxDateLimit.value);
// 每次显示前重新渲染全部列
getcolumns();
}
},
{
immediate: true // 立即监听--进入就会执行一次 监听显影状态
}
);
// 时间选择器关闭 值不改变并关闭弹框
function cancelOn() {
emit('cancel');
}
// 时间选择器确定 值改变
function onConfirm() {
// 注意data.selectedValues比selectedValues更准确
// 拼接数据
const splitArr = ['-', '-', ' ', ':', ':'];
let iiindex = -1;
const changeValue = data.selectedValues.reduce((a, b) => {
iiindex += 1;
return a + splitArr[iiindex] + b;
});
emit('confirm', changeValue);
}
</script>

View File

@@ -23,7 +23,7 @@ const props = defineProps({
const editor = ref(null);
const editorAction = ref(null);
const emit = defineEmits(['update:modelValue']);
const emit = defineEmits(['update:modelValue', 'blur']);
const save = (e) => {
emit('update:modelValue', e.innerHTML);
};
@@ -42,6 +42,9 @@ const functions = {
};
const funEvent = (item) => {
// 保持焦点在编辑器
const selection = window.getSelection();
selection.getRangeAt(0);
functions[item.fun]();
};
@@ -81,12 +84,40 @@ const actions = [
fun: 'italic'
}
];
const checkContains = (element, target) => {
try {
return element?.contains(target) ?? false;
} catch (e) {
console.error('Contains check failed:', e);
return false;
}
};
onMounted(() => {
editor.value.addEventListener('focus', () => {
showAction.value = true;
});
// 如果点击了 editor 与 editorAction 其他地方就触发保存
document.addEventListener('click', (e) => {
if (!editor.value || !editorAction.value) return;
const target = e.composedPath?.()?.[0] || e.target;
const isEditor = checkContains(editor.value, target);
const isActionBar = checkContains(editorAction.value, target);
if (!isEditor && !isActionBar) {
showAction.value = false;
save(editor.value);
emit('blur', editor.value);
}
});
// editor.value.addEventListener('blur', () => {
// setTimeout(() => {
// showAction.value = false;
// });
// });
document.addEventListener('resize', () => {
showAction.value = false;
});

12
src/config.js Normal file
View File

@@ -0,0 +1,12 @@
// src/config.js
export default {
proxyUrl: import.meta.env.VITE_APP_BASEURL,
proxyUrlDelivery: import.meta.env.VITE_APP_DELIVERY_BASEURL,
proxyUrlMessageCenter: import.meta.env.VITE_APP_MESSAGE_CENTER,
baseOss: import.meta.env.VITE_APP_BASEOSS,
loginUrl: import.meta.env.VITE_APP_LOGIN,
socketUrl: import.meta.env.VITE_APP_SOCKETURL,
jsonpUrl: import.meta.env.VITE_APP_JSONPURL,
jqrUrl: import.meta.env.VITE_APP_YQRURL,
currentMode: import.meta.env.VITE_APP_CURRENTMODE
};

View File

@@ -9,6 +9,14 @@
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont')
format('svg');
src:
url('//at.alicdn.com/t/c/font_4841764_vat2jbvw3q.woff2?t=1741575060989') format('woff2'),
url('//at.alicdn.com/t/c/font_4841764_vat2jbvw3q.woff?t=1741575060989') format('woff'),
url('//at.alicdn.com/t/c/font_4841764_vat2jbvw3q.ttf?t=1741575060989') format('truetype');
}
@font-face {
font-family: '';
}
.logo {

548
src/fonts/moblie/demo.css Normal file
View File

@@ -0,0 +1,548 @@
/* Logo 字体 */
@font-face {
font-family: 'iconfont logo';
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src:
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix')
format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont')
format('svg');
}
.logo {
font-style: normal;
font-size: 160px;
font-family: 'iconfont logo';
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
color: #666;
line-height: 42px;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
position: relative;
z-index: 1;
width: 100px;
height: 40px;
margin-bottom: -1px;
border-bottom: 2px solid transparent;
color: #666;
font-size: 16px;
line-height: 40px;
text-align: center;
cursor: pointer;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
width: 960px;
margin: 0 auto;
padding: 30px 100px;
}
.main .logo {
overflow: hidden;
height: 110px;
margin-top: -50px;
margin-bottom: 30px;
color: #333;
line-height: 1;
text-align: left;
*zoom: 1;
}
.main .logo a {
color: #333;
font-size: 160px;
}
.helps {
margin-top: 40px;
}
.helps pre {
overflow: auto;
margin: 10px 0;
padding: 20px;
border: solid 1px #e7e1cd;
background-color: #fffdef;
}
.icon_lists {
overflow: hidden;
width: 100% !important;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-right: 20px;
margin-bottom: 10px;
list-style: none !important;
text-align: center;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
margin: 10px auto;
color: #333;
font-size: 42px;
line-height: 100px;
-webkit-transition:
font-size 0.25s linear,
width 0.25s linear;
-moz-transition:
font-size 0.25s linear,
width 0.25s linear;
transition:
font-size 0.25s linear,
width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentcolor;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
margin-bottom: 24px;
color: #404040;
font-weight: 500;
line-height: 40px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
clear: both;
margin: 1.6em 0 0.6em;
color: #404040;
font-weight: 500;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
clear: both;
height: 1px;
margin: 16px 0;
border: 0;
background: #e9e9e9;
}
.markdown p {
margin: 1em 0;
}
.markdown > p,
.markdown > blockquote,
.markdown > .highlight,
.markdown > ol,
.markdown > ul {
width: 80%;
}
.markdown ul > li {
list-style: circle;
}
.markdown > ul li,
.markdown blockquote ul > li {
margin-left: 20px;
padding-left: 4px;
}
.markdown > ul li p,
.markdown > ol li p {
margin: 0.6em 0;
}
.markdown ol > li {
list-style: decimal;
}
.markdown > ol li,
.markdown blockquote ol > li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
border-radius: 3px;
background: #eee;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown > table {
width: 95%;
margin-bottom: 24px;
border: 1px solid #e9e9e9;
border-spacing: 0;
border-collapse: collapse;
empty-cells: show;
}
.markdown > table th {
color: #333;
font-weight: 600;
white-space: nowrap;
}
.markdown > table th,
.markdown > table td {
padding: 8px 16px;
border: 1px solid #e9e9e9;
text-align: left;
}
.markdown > table th {
background: #f7f7f7;
}
.markdown blockquote {
margin: 1em 0;
padding-left: 0.8em;
border-left: 4px solid #e9e9e9;
color: #999;
font-size: 90%;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
margin-left: 8px;
opacity: 0;
transition: opacity 0.3s ease;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
display: inline-block;
opacity: 1;
}
.markdown > br,
.markdown > p > br {
clear: both;
}
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
background: white;
color: #333;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
background-color: #eaffea;
color: #55a532;
}
.hljs-deletion {
background-color: #ffecec;
color: #bd2c00;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*='language-'],
pre[class*='language-'] {
background: none;
color: black;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
line-height: 1.5;
text-align: left;
text-shadow: 0 1px white;
white-space: pre;
word-spacing: normal;
word-wrap: normal;
word-break: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*='language-']::-moz-selection,
pre[class*='language-'] ::-moz-selection,
code[class*='language-']::-moz-selection,
code[class*='language-'] ::-moz-selection {
background: #b3d4fc;
text-shadow: none;
}
pre[class*='language-']::selection,
pre[class*='language-'] ::selection,
code[class*='language-']::selection,
code[class*='language-'] ::selection {
background: #b3d4fc;
text-shadow: none;
}
@media print {
code[class*='language-'],
pre[class*='language-'] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*='language-'] {
overflow: auto;
margin: 0.5em 0;
padding: 1em;
}
:not(pre) > code[class*='language-'],
pre[class*='language-'] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*='language-'] {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: 0.7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
background: hsla(0deg, 0%, 100%, 0.5);
color: #9a6e3a;
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #dd4a68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View File

@@ -0,0 +1,75 @@
@font-face {
font-family: mobilefont; /* Project id 4841764 */
src:
url('iconfont.woff2?t=1741575354833') format('woff2'),
url('iconfont.woff?t=1741575354833') format('woff'),
url('iconfont.ttf?t=1741575354833') format('truetype');
}
.mobilefont {
font-style: normal;
font-size: 18px;
font-family: mobilefont !important;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-radiobox::before {
content: '\e75b';
}
.icon-juzhendafen::before {
content: '\e641';
}
.icon-checkbox-checked::before {
content: '\e6c3';
}
.icon-nps::before {
content: '\e6b0';
}
.icon-input::before {
content: '\e6fd';
}
.icon-juzhentiankong::before {
content: '\e62e';
}
.icon-wenjianshangchuan::before {
content: '\e631';
}
.icon-qianming::before {
content: '\e661';
}
.icon-tuwen::before {
content: '\e62c';
}
.icon-juzhenduoxuan::before {
content: '\e818';
}
.icon-juzhendanxuan::before {
content: '\13c7f';
}
.icon-edit2::before {
content: '\e630';
}
.icon-copy::before {
content: '\e632';
}
.icon-delete::before {
content: '\e63f';
}
.icon-sort::before {
content: '\e6a0';
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,114 @@
{
"id": "4841764",
"name": "yl",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "240132",
"name": "radio_box",
"font_class": "radiobox",
"unicode": "e75b",
"unicode_decimal": 59227
},
{
"icon_id": "844024",
"name": "矩阵打分",
"font_class": "juzhendafen",
"unicode": "e641",
"unicode_decimal": 58945
},
{
"icon_id": "1176868",
"name": "checkbox-checked",
"font_class": "checkbox-checked",
"unicode": "e6c3",
"unicode_decimal": 59075
},
{
"icon_id": "8766356",
"name": "nps",
"font_class": "nps",
"unicode": "e6b0",
"unicode_decimal": 59056
},
{
"icon_id": "11215250",
"name": "input",
"font_class": "input",
"unicode": "e6fd",
"unicode_decimal": 59133
},
{
"icon_id": "15969322",
"name": "矩阵填空",
"font_class": "juzhentiankong",
"unicode": "e62e",
"unicode_decimal": 58926
},
{
"icon_id": "15969340",
"name": "文件上传",
"font_class": "wenjianshangchuan",
"unicode": "e631",
"unicode_decimal": 58929
},
{
"icon_id": "17269853",
"name": "签名",
"font_class": "qianming",
"unicode": "e661",
"unicode_decimal": 58977
},
{
"icon_id": "17373529",
"name": "图文",
"font_class": "tuwen",
"unicode": "e62c",
"unicode_decimal": 58924
},
{
"icon_id": "36443758",
"name": "矩阵多选",
"font_class": "juzhenduoxuan",
"unicode": "e818",
"unicode_decimal": 59416
},
{
"icon_id": "38465935",
"name": "矩阵单选",
"font_class": "juzhendanxuan",
"unicode": "13c7f",
"unicode_decimal": 81023
},
{
"icon_id": "1160114",
"name": "edit-2",
"font_class": "edit2",
"unicode": "e630",
"unicode_decimal": 58928
},
{
"icon_id": "1160116",
"name": "copy",
"font_class": "copy",
"unicode": "e632",
"unicode_decimal": 58930
},
{
"icon_id": "1160129",
"name": "delete",
"font_class": "delete",
"unicode": "e63f",
"unicode_decimal": 58943
},
{
"icon_id": "1160227",
"name": "sort",
"font_class": "sort",
"unicode": "e6a0",
"unicode_decimal": 59040
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -15,7 +15,10 @@ const modules = Object.entries(modulesFiles).reduce(
export const useCounterStore = defineStore('counter', () => {
// 引入 common 模块
const commonStore = useCommonStore();
const { questionsInfo } = storeToRefs(commonStore);
const ques = storeToRefs(commonStore);
// 暴露 fetchQuestionInfo 方法
const { fetchQuestionInfo, setQuestionInfo } = commonStore;
// 返回需要暴露的内容
return { commonStore, questionsInfo, modules };
return { commonStore, modules, fetchQuestionInfo, ...ques, setQuestionInfo };
});

View File

@@ -4,40 +4,25 @@ import { defineStore } from 'pinia';
// 如果没有现成的 uid 函数,可以引入一个 UUID 库
// 或者使用其他 UUID 库
import { v4 as uuidv4 } from 'uuid';
import { http } from '@/request/axios';
import { surveyQuestion } from '@/request/api/modules/survey';
export const useCommonStore = defineStore('common', {
state: () => ({
questionsInfo: {
survey: {
id: 8721,
introduction: '<p><span style="color: #e03e2d;">【样本量要求&ge;60】</span></p>',
pages: [
[24, 27, 26, 1],
[2, 3, 4, 5, 6, 7],
[8, 9, 10, 11, 12, 13, 14],
[15, 16, 17, 18, 19, 20, 21],
[28, 22]
],
sn: '8YXK4kW5',
id: 9577,
introduction: '<p>123</p>',
pages: [[]],
sn: 'oxywX8W6',
status: 0,
title:
'<p><span style="font-size: 24px;"><span style="font-family: \'PingFang SC Regular\';">液态奶产品研究标准化问卷</span><span style="font-family: \'Arial Unicode MS\';">-</span><span style="font-family: \'PingFang SC Regular\';">概念测试(内测标准版)</span></span></p>',
detail_pages: [
[24, 27, 26, 1],
[2, 3, 4, 5, 6, 7],
[8, 9, 10, 11, 12, 13, 14],
[15, 16, 17, 18, 19, 20, 21],
[28, 22]
],
title: '报名签到问卷 ',
detail_pages: [],
group_pages: [],
is_one_page_one_question: 0,
last_question_index: 0,
is_three_d_permissions: 0,
is_dept: 1,
last_title: 'Q5',
project_name: '概念诊断问卷示例-0227',
last_title: 'Q0',
project_name: '报名签到问卷 ',
quota_end_content:
'<p style="text-align:center"><img style="width:220px;margin-top:30px;margin-bottom: 40px;" src="https://cxp-pubcos.yili.com/prod-yls/theme/XxgQ98WN/1693807609602_962_error.png"></p>\n<p style="text-align:center;font-size: 16px;font-weight: 500;padding-bottom: 12px;/* margin-bottom: 10px; */">很抱歉,本次调研不太适合您的情况,感谢您的参与!</p>',
quota_end_url: '',
@@ -52,225 +37,27 @@ export const useCommonStore = defineStore('common', {
end_jump_status: 0,
end_jump_standing_time: 0,
success_end_content:
'<p style="text-align:center"><img style="width:220px;margin-top:30px;margin-bottom: 40px;" src="https://cxp-pubcos.yili.com/prod-yls/theme/XxgQ98WN/1693807609607_514_success.png"></p>\n<p style="text-align:center;font-size: 16px;font-weight: 500;padding-bottom: 12px;/* margin-bottom: 10px; */">您已完成本次调研,感谢您的参与!</p>\n<p style="text-align:center;color: #85b43a;font-size: 16px;font-weight: 550;">【成功完成】</p>',
'<p style="text-align:center"><img style="width:220px;margin-top:30px;margin-bottom: 40px;" src="https://cxp-pubcos.yili.com/prod-yls/theme/XxgQ98WN/1693807609607_514_success.png"></p>\n<p style="text-align:center;font-size: 16px;font-weight: 500;padding-bottom: 12px;/* margin-bottom: 10px; */">您已完成本次调研,感谢您的参与!</p>\n<p style="text-align:center;color: 85b43a;font-size: 16px;font-weight: 550;">【成功完成】</p>',
success_end_url: '',
success_end_url_select: 0,
success_standing_time: 0,
template_type: 300,
local_pages: [
{
pages: [],
is_short_time: 0,
short_time: '',
is_show: 0,
use_type: 0
template_type: 0,
local_pages: []
},
{
pages: [],
is_short_time: 0,
short_time: '',
is_show: 0,
use_type: 0
},
{
pages: [],
is_short_time: 0,
short_time: '',
is_show: 0,
use_type: 0
},
{
pages: [],
is_short_time: 0,
short_time: '',
is_show: 0,
use_type: 0
},
{
pages: [],
is_short_time: 0,
short_time: '',
is_show: 0,
use_type: 0
}
]
},
logics: [
{
logic: [
{
value: '',
location: 0,
date: '',
time: '',
type: 0,
row_type: 0,
cell_type: 0,
logic: 'if',
operator: '=',
is_answer: 1,
is_select: 0,
row_index: 0,
cell_index: 0,
question_type: 1,
question_index: 24,
relation_question_index: 0,
relation_question_row_index: 0,
relation_question_cell_index: 0,
is_option_group: 0,
option_index: 1,
skip_type: null,
question_id: null
}
],
skip_question_index: 27,
skip_type: 0,
id: 472148,
question_index: 24,
question_id: '17852294'
},
{
logic: [
{
value: '',
location: 0,
date: '',
time: '',
type: 0,
row_type: 0,
cell_type: 0,
logic: 'if',
operator: '=',
is_answer: 1,
is_select: 0,
row_index: 0,
cell_index: 0,
question_type: 1,
question_index: 24,
relation_question_index: 0,
relation_question_row_index: 0,
relation_question_cell_index: 0,
is_option_group: 0,
option_index: 0,
skip_type: null,
question_id: null
}
],
skip_type: 0,
id: 472149,
question_index: 24,
question_id: '17852294'
},
{
logic: [
{
value: '',
location: 0,
date: '',
time: '',
type: 0,
row_type: 0,
cell_type: 0,
logic: 'if',
operator: '=',
is_answer: 1,
is_select: 0,
row_index: 0,
cell_index: 0,
question_type: 1,
question_index: 24,
relation_question_index: 0,
relation_question_row_index: 0,
relation_question_cell_index: 0,
is_option_group: 0,
option_index: 0,
skip_type: null,
question_id: null
}
],
skip_type: 0,
id: 472151,
question_index: 24,
question_id: '17852294'
},
{
logic: [
{
logic: 'if',
question_index: 30,
question_type: 1,
is_answer: 1,
operator: '=',
option_index: 2,
relation_question_index: 0,
type: 0,
is_option_group: 0,
group_index: null
},
{
logic: 'and',
question_index: 30,
question_type: 1,
is_answer: 1,
operator: '=',
option_index: 1,
relation_question_index: 0,
type: 0,
is_option_group: 1,
group_index: 1
}
],
skip_question_index: 0,
skip_type: 1,
id: 472152,
question_index: 24,
question_id: '17852294'
},
{
logic: [
{
logic: 'if',
question_index: 30,
question_type: 1,
is_answer: 0,
operator: '=',
option_index: 0,
relation_question_index: 0,
type: 0
}
],
skip_type: 1,
id: '28fe4d64-81eb-4937-a082-c519ac74374f',
question_index: 24,
question_id: '17852294'
},
{
logic: [
{
operator: '=',
is_answer: 1,
question_type: 1,
logic: 'always'
}
],
skip_type: 1,
id: '459f781f-8db0-4663-b2d1-9ddad627078e',
question_index: 24,
question_id: '17852294'
}
],
logics: [],
questions: [],
cycle_pages: null
} as QuestionsInfo
}
}),
actions: {
async fetchQuestionInfo(questionInfo: string) {
async fetchQuestionInfo(questionInfo) {
console.log(questionInfo, 456);
try {
if (!questionInfo) return;
const info = JSON.parse(questionInfo);
if (info?.questions?.length) {
info.questions.forEach((page:any) => {
info.questions.forEach((page) => {
// 使用 UUID 生成唯一 ID
if (page?.page) {
page.pageId = page.pageId || uuidv4();
@@ -282,21 +69,10 @@ export const useCommonStore = defineStore('common', {
// 未来扩展
}
},
setQuestionInfo(questionInfo: QuestionsInfo) {
setQuestionInfo(questionInfo) {
console.log(questionInfo, 9998);
console.log(this);
this.questionsInfo = questionInfo;
},
/**
* web 端测试问卷地址 `/api/api/console/surveys/RWNK9BYp/questions`
* 做了个测试,后面再改
*/
fetchSurveyQuestion() {
http.get(surveyQuestion).then(res => {
// console.log('http get request:',res.data.data);
this.setQuestionInfo(res.data.data);
});/* .catch(res=>{
// console.log('catch',res);
this.setQuestionInfo(res);
}) */
}
},
getters: {

View File

@@ -0,0 +1,29 @@
export default {
id: '',
question_type: 18,
question_index: 41,
stem: '请上传文件',
title: 'Q14',
options: [],
last_option_index: 0,
config: {
is_required: 1,
quick_type: 0,
is_show: [],
select_random: 0,
min_number: 1,
max_number: 1,
min_size: 0,
max_size: 1,
is_file: 0,
file_type: '0'
},
associate: [],
question_code: '',
logic_config: {
order: 0,
type: 0,
expect: '',
stay_time: ''
}
};

View File

@@ -1,14 +1,14 @@
export default {
// JSON 需要修改
id: '',
question_type: 5,
question_type: '',
question_index: '',
stem: '请完成打分',
stem: '请每行选择一个选项',
title: '',
options: [
[
{
id: 'e46f51b1-bfd8-4d9c-becc-4fb7d175a6f4',
id: '',
is_fixed: 0,
is_other: 0,
is_remove_other: 0,
@@ -25,6 +25,83 @@ export default {
type: 0,
cascade: [],
config: []
},
{
id: '',
is_fixed: 0,
is_other: 0,
is_remove_other: 0,
option: '<p>选项2</p>',
option_config: {
image_url: [],
title: '',
instructions: [],
option_type: 0,
limit_right_content: ''
},
option_index: 1,
parent_id: 0,
type: 0,
cascade: [],
config: []
}
],
[
{
option: '<p style="text-align:center">列标签1</p>',
id: '1049201',
type: 2,
is_other: 0,
is_fixed: 0,
is_remove_other: 0,
created_at: null,
created_user_id: null,
parent_id: null,
option_index: 3,
list_id: 74491,
option_code: '',
option_config: {
title: '',
instructions: [],
price: 0,
gradient: '',
image_url: [],
option_type: 0,
type: 0,
limit_right_content: '',
child_area: null,
binding_goods_id: ''
},
disable_option_update: null,
cascade: []
},
{
option: '<p style="text-align:center">列标签2</p>',
id: '1049202',
type: 2,
is_other: 0,
is_fixed: 0,
is_remove_other: 0,
created_at: null,
created_user_id: null,
parent_id: null,
option_index: 4,
list_id: 74491,
option_code: '',
option_config: {
title: '',
instructions: [],
price: 0,
gradient: '',
image_url: [],
option_type: 0,
type: 0,
limit_right_content: '',
child_area: null,
binding_goods_id: ''
},
disable_option_update: null,
cascade: []
}
]
],

View File

@@ -0,0 +1,54 @@
export default {
id: '',
question_type: 106,
question_index: 0,
stem: '您向朋友或同事推荐我们的可能性多大?',
title: '',
options: [
[
{
id: 'b68d45eb-d833-4b25-b0aa-2fde1310e88d',
is_fixed: 0,
is_other: 0,
is_remove_other: 0,
option: '<p>选项1</p>',
option_config: {
image_url: [],
title: '',
instructions: [],
option_type: 0,
limit_right_content: ''
},
option_index: 1,
parent_id: 0,
type: 0,
cascade: [],
config: []
}
]
],
last_option_index: 0,
config: {
is_required: 1,
quick_type: 0,
is_show: [],
version: 2,
prompt_left: '不可能',
prompt_right: '极有可能',
prompt_center: '',
max: 10,
min: 0,
score_interval: 1,
score_type: 0,
score_way: 1,
prompt_score: 2
},
associate: [],
question_code: '',
logic_config: {
order: 0,
type: 0,
expect: '',
stay_time: ''
}
};

View File

@@ -0,0 +1,37 @@
export default {
id: '17852690',
title: 'Q7',
stem: '请留下您的姓名',
other: '',
question_index: 10,
question_type: 22,
config: {
is_required: 1,
select_random: 0,
float_window: 0,
float_window_content: '',
popup_window: 0,
popup_window_content: '',
is_show: [],
quick_type: 0
},
created_at: '2025-03-06T15:51:13',
created_user_id: 1281,
updated_user_id: null,
survey_id: 9482,
logic_config: {
expect: '',
order: 0,
type: 0,
stay_time: ''
},
options: [],
associate: [],
logics_has: null,
last_option_index: 0,
question_code: '',
question_value: '',
question_tag: '',
planet_id: '',
permissions: null
};

View File

@@ -0,0 +1,22 @@
export default {
id: '',
question_type: '',
question_index: 0,
stem: '请认真阅读以下内容',
title: '',
options: [],
last_option_index: 0,
config: {
is_required: 1,
quick_type: 0,
is_show: []
},
associate: [],
question_code: '',
logic_config: {
order: 0,
type: 0,
expect: '',
stay_time: ''
}
};

View File

@@ -4,3 +4,7 @@ export { default as checkbox } from './QuestionJsons/Checkbox.js';
export { default as completion } from './QuestionJsons/Completion.js';
export { default as rate } from './QuestionJsons/Rate.js';
export { default as martrixQuestion } from './QuestionJsons/MartrixQuestion.js';
export { default as fileUpload } from './QuestionJsons/FileUpload.js';
export { default as textWithImages } from './QuestionJsons/TextWithImages.js';
export { default as signQuestion } from './QuestionJsons/SignQuestion.js';
export { default as nps } from './QuestionJsons/NPS.js';

91
src/utils/request.js Normal file
View File

@@ -0,0 +1,91 @@
import axios from 'axios';
// import router from '@/router/index';
// import { A_COMMON_CLEAR_TOKEN } from '@/stores/constance/constance.common.js';
import * as config from '@/config.js';
// import {proxyUrl} from config.default
const NODE_ENV = import.meta.env.VITE_APP_ENV;
const baseURL = NODE_ENV === 'production' ? config.default.proxyUrl : 'http://192.168.8.165:15011/';
// axios.defaults.withCredentials = true;
// create an axios instance
const service = axios.create({
baseURL: `${baseURL}`, // url = base url + request url
// withCredentials: true, // send cookies when cross-domain requests
timeout: 30000 // request timeout
});
// request interceptor
service.interceptors.request.use(
(config) => {
if (!config.headers) {
config.headers.Accept = 'application/json';
}
config.headers.Authorization = `${localStorage.getItem('plantToken')}`;
if (!config.headers.remoteIp) {
config.baseURL += '/api';
}
delete config.headers.host;
config.headers.remoteIp = localStorage.getItem('plantIp') || '127.0.0.1';
// if (store.state.common.token) {
// config.headers['Login-Type'] = 'pc';
// config.headers.Authorization = `Bearer ${store.state.common.token}`;
// }
return config;
},
(error) => Promise.reject(error)
);
// response interceptor
service.interceptors.response.use(
(response) => {
if (
response.status === 200
|| response.status === 201
|| response.status === 202
|| response.status === 204
) {
if (response.config.method === 'put') {
// message.success('保存中...');
}
return Promise.resolve(response);
}
// return Promise.reject(/* new Error(response.message || 'Error') */);
}
// (error) => {
// // for debug
// if (error.response.status === 401) {
// const query = router.currentRoute.value.query;
// //关闭已弹出的所有弹框,防止弹框重叠
// // Modal.destroyAll();
// store.dispatch(A_COMMON_CLEAR_TOKEN);
// window.parent.postMessage(
// {
// code: '301',
// params: {}
// },
// '*'
// );
// store.commit('common/M_COMMON_SET_TOKEN_UNAUTHORIZED', false);
// } else if (error.response.status === 403) {
// router.push({
// path: '/error/403'
// });
// } else if (error.response.status === 404) {
// router.push({
// path: '/error/404'
// });
// } else if (error.response.status === 500) {
// router.push({
// path: '/error/500'
// });
// } else {
// // message.error(error.response.data?.message || '服务器错误');
// }
// return Promise.reject(error.response);
// }
);
export default service;

View File

@@ -14,8 +14,6 @@
:questions="questionInfo.questions"
:index="index"
:chooseQuestionId="chooseQuestionId"
:show-actions="!preview"
style="margin: 10px 0"
@get-choose-question-id="getChooseQuestionId"
>
<!-- 选择题 -->
@@ -89,7 +87,11 @@
<template #action="{ element: el }">
<div class="flex slot-actions">
<template v-for="(item, optionIndex) in actionOptions">
<div v-if="item.question_type.includes(el.question_type)" :key="optionIndex" class="flex">
<div
v-if="item.question_type.includes(el.question_type)"
:key="optionIndex"
class="flex"
>
<template v-for="(act, actIndex) in item.actions" :key="actIndex">
<div class="flex align-center action-item" @click="actionEvent(act, el)">
<van-icon :name="act.icon"></van-icon>
@@ -104,10 +106,13 @@
<!-- {{ element.question_type }}-->
<!-- {{questionInfo.survey.pages.length}}-->
<div v-if="!preview">
<div v-if="!filterGap">
<paging
v-if="!element.question_type && questionInfo.survey.pages.length > 1" :info="element" :index="index"
:active="pageIsActive(activeIndex, questionInfo.questions, element.page)" @click.stop=""
v-if="!element.question_type && questionInfo.survey.pages.length > 1"
:info="element"
:index="index"
:active="pageIsActive(activeIndex, questionInfo.questions, element.page)"
@click.stop=""
/>
</div>
</template>
@@ -116,7 +121,7 @@
</template>
<script setup>
import { v4 as uuidv4 } from 'uuid';
import { ref, onMounted } from 'vue';
import { ref, onMounted, watch } from 'vue';
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
import Draggable from './components/Draggable.vue';
@@ -134,6 +139,26 @@ import { useCommonStore } from '@/stores/modules/common';
const activeIndex = ref(-1);
// 获取所有的 question 列表内容
const { filterGap, activeId } = defineProps({
filterGap: {
type: Boolean,
required: false,
default: false
},
activeId: {
type: String,
default: ''
}
});
watch(
() => activeId,
(newVal) => {
chooseQuestionId.value = newVal;
}
);
/**
* 工具函数
*/
@@ -180,19 +205,6 @@ function util() {
};
}
/**
* 该组件用于展示和操作问卷中的问题。
* @description 过滤Gap栏
* @type {boolean} 默认为 false
*/
const { preview } = defineProps({
preview: {
type: Boolean,
required: false,
default: false
}
});
const { pageIsActive } = util();
// 获取 Store 实例
const counterStore = useCounterStore();

View File

@@ -13,9 +13,13 @@
<contenteditable v-model="element.stem" :active="active"></contenteditable>
</template>
<template #input>
<template v-for="(item, index) in element.options" :key="index">
<template v-for="(item, optionIndex) in element.options" :key="item.id">
<van-radio-group v-if="element.question_type === 1">
<option-action v-model:data="element.options[index]" :active="active" :question="element">
<option-action
v-model:data="element.options[optionIndex]"
:active="active"
:question="element"
>
<template #item="{ element: it, index: itIndex }">
<van-radio
:key="itIndex"

View File

@@ -0,0 +1,211 @@
<script setup lang="ts">
import { useTemplateRef, ref, type Directive } from 'vue';
const columnLabels = useTemplateRef<HTMLElement[]>('columnLabels');
// 注意, element.options 里面的东西是数组,第一项内容是行标签内容,第二项内容是列标签内容
// 类型 AI 生成 切勿盲目相信,以实际为准
const { element, index } = defineProps<{ element: MatrixSurveyQuestion, index: number }>();
const rowRecord = new Array(element.options[0].length);
// matrix 答案
const matrixAnswer = ref({
question_index: index,
answer: {} as any
});
/**
* input 类型映射,里面自行处理逻辑返回对应类型
* // remark: 填空内容 question_type 8
* // remark: 单选打分矩阵 question_type 9
* // remark: 多选矩阵内容 question_type 10
* @default 'radio'
*/
const tableInputTypeMapping = (/** regx?: any */) => {
switch (element.question_type) {
case 8:
return 'text';
case 9:
return 'radio';
case 10:
return 'checkbox';
default:
return 'radio';
}
};
/**
* 自定义指令,用于在元素挂载后自动获取焦点
*/
const vFocus: Directive = {
mounted(el: HTMLInputElement) {
el.focus();
}
};
/**
* row 的数值变动之后,触发的事件
* @param {string} value
* @return {void}
*/
function handleRowNameChange(/* value: string */) {
// if (!value) return;
console.log(`row change`);
}
/**
* col 的数值变动之后,触发的事件
*/
function handleColNameChange(rowOption: string, colOption: string, e: any) {
// if (!value) return;
const col = element.options[0].findIndex(option => {
return option.option === colOption;
});
const row = element.options[1].findIndex(option => {
return option.option === rowOption;
});
console.log(`${col + 1}_${row + 1}`);
// 不同的 question_type 的 matrix 问卷处理不同的结果
switch (element.question_type) {
case 8: {
// 获取输入框元素
const inputElement = e.target as HTMLInputElement;
// 如果没有获取到输入框元素,则直接返回
if (!inputElement) return;
// 将输入框的值保存到 rowRecord 对应位置
rowRecord[col] = e!.target!.value;
// 清空 matrixAnswer 的 answer 属性
matrixAnswer.value.answer = {};
// 遍历所有行选项
element.options[0].forEach((_, rowIndex) => {
// 获取当前行记录
const colOptions = rowRecord[rowIndex];
// 如果当前行有记录,则更新 matrixAnswer 的 answer 属性
if (colOptions) {
matrixAnswer.value.answer[`R${rowIndex + 1}_C${col + 1}`] = colOptions;
}
});
break;
}
case 9:
// 将选择的行索引加1后保存到 rowRecord 对应位置
rowRecord[col] = row + 1;
// 清空 matrixAnswer 的 answer 属性
matrixAnswer.value.answer = {};
// 遍历 rowRecord更新 matrixAnswer 的 answer 属性
rowRecord.forEach((row, index) => {
matrixAnswer.value.answer[`${index + 1}_${row}`] = 1;
});
break;
case 10:
// 将选择的行索引加1后添加到 rowRecord 对应位置的数组中
rowRecord[col] = (rowRecord[col] || []).concat(row + 1);
// 清空 matrixAnswer 的 answer 属性
matrixAnswer.value.answer = {};
// 遍历所有行选项
element.options[0].forEach((rowOption, rowIndex) => {
// 获取当前行记录
const colOptions = rowRecord[rowIndex];
// 如果当前行有记录,则更新 matrixAnswer 的 answer 属性
if (colOptions) {
colOptions.forEach((col: any) => {
matrixAnswer.value.answer[`R${rowIndex + 1}_C${col}`] = true;
});
}
});
break;
default:
break;
}
}
</script>
<template>
<van-cell>
<!-- 使用 title 插槽来自定义标题 -->
<template #title>
<span>
<span v-if="element?.config?.is_required">*</span>
<span v-html="element.title"></span>
<span v-html="element.stem"></span>
</span>
</template>
<!-- 使用 label 插槽来自定义标题 -->
<template #label>
<table class="matrix-table">
<thead>
<tr>
<!-- 第一行内容为空 -->
<th></th>
<!-- 第二行内容开始填充 -->
<td v-for="col in element.options[1]" :key="col.option" ref="columnLabels">
<!-- 编辑状态单次点击出输入框失焦后关闭 -->
<input
v-if="col.editor" v-model="col.option" v-focus type="text" @focusout="col.editor = false"
@click="handleRowNameChange(col.option!)"
/>
<span v-else @click="col.editor = true" v-html="col.option"></span>
</td>
</tr>
</thead>
<tbody>
<tr v-for="(row) in element.options[0]" :key="row.option">
<!-- 编辑状态单次点击出输入框失焦后关闭 -->
<td>
<input v-if="row.editor" v-model="row.option" v-focus type="text" @focusout="row.editor = false" />
<span v-else @click="row.editor = true" v-html="row.option"></span>
</td>
<td v-for="col in element.options[1]" :key="col.option">
<!-- 编辑状态单次点击出输入框失焦后关闭 -->
<input
:id="col.option" :type="tableInputTypeMapping()" :name="row.option"
@change="handleColNameChange(col.option!, row.option!,$event)"
/>
</td>
</tr>
</tbody>
</table>
</template>
</van-cell>
</template>
<style scoped lang="scss">
.matrix-table {
width: 100%;
border-collapse: collapse;
color: black;
th,
td {
padding: 8px;
border-width: 0 0 1px;
text-align: left;
}
}
input[type='text'] {
width: 85%;
}
.martrix-table-action {
margin-top: 10px;
.van-icon {
color: lightgreen;
font-size: 12px;
}
.martrix-table-action-tool {
display: flex;
justify-content: flex-end;
& > span {
margin-right: 6px;
font-size: 16px;
}
}
}
</style>

View File

@@ -19,12 +19,11 @@
</template>
<template #input>
<div v-for="(optionItem, optionItemIndex) in element.options" :key="optionItemIndex">
<div v-for="(item, index) in optionItem" :key="index" @click="chooseOption(item)">
<!-- <div-->
<!-- :contenteditable="item.id === chooseId"-->
<!-- class="van-field"-->
<!-- v-html="item.option"-->
<!-- ></div>-->
<div
v-for="(item, optionIndex) in optionItem"
:key="optionIndex"
@click="chooseOption(item)"
>
<RateCharacter :config="element.config"></RateCharacter>
<div class="tips">
<p>{{ element.config.prompt_left }}</p>

View File

@@ -19,7 +19,11 @@
</template>
<template #input>
<div v-for="(optionItem, optionItemIndex) in element.options" :key="optionItemIndex">
<div v-for="(item, index) in optionItem" :key="index" @click="chooseOption(item)">
<div
v-for="(item, optionIndex) in optionItem"
:key="optionIndex"
@click="chooseOption(item)"
>
<div
:contenteditable="item.id === chooseId"
class="van-field"

View File

@@ -15,7 +15,12 @@ const currentStep = ref(-1);
// 保存当前状态
const saveState = () => {
if (!ctx || !signatureCanvas.value) return;
const imageData = ctx.getImageData(0, 0, signatureCanvas.value.width, signatureCanvas.value.height);
const imageData = ctx.getImageData(
0,
0,
signatureCanvas.value.width,
signatureCanvas.value.height
);
currentStep.value += 1;
// 移除当前步骤之后的所有状态(处理在撤销后又进行了新的绘制的情况)
undoStack.value.splice(currentStep.value);
@@ -58,15 +63,13 @@ onMounted(() => {
// 触摸开始,开始绘制适用于移动设备
signatureCanvas.value?.addEventListener('touchstart', (e) => {
e.preventDefault(); // 防止页面滚动
// 防止页面滚动
e.preventDefault();
isDrawing = true;
const rect = signatureCanvas.value!.getBoundingClientRect();
const touch = e.touches[0];
ctx.beginPath();
ctx.moveTo(
touch.clientX - rect.left,
touch.clientY - rect.top
);
ctx.moveTo(touch.clientX - rect.left, touch.clientY - rect.top);
});
signatureCanvas.value?.addEventListener('touchmove', (e) => {
@@ -74,10 +77,7 @@ onMounted(() => {
if (!isDrawing) return;
const rect = signatureCanvas.value!.getBoundingClientRect();
const touch = e.touches[0];
ctx.lineTo(
touch.clientX - rect.left,
touch.clientY - rect.top
);
ctx.lineTo(touch.clientX - rect.left, touch.clientY - rect.top);
ctx.stroke();
});
@@ -97,19 +97,13 @@ onMounted(() => {
isDrawing = true;
const rect = signatureCanvas.value!.getBoundingClientRect();
ctx.beginPath();
ctx.moveTo(
e.clientX - rect.left,
e.clientY - rect.top
);
ctx.moveTo(e.clientX - rect.left, e.clientY - rect.top);
});
signatureCanvas.value?.addEventListener('mousemove', (e) => {
if (!isDrawing) return;
const rect = signatureCanvas.value!.getBoundingClientRect();
ctx.lineTo(
e.clientX - rect.left,
e.clientY - rect.top
);
ctx.lineTo(e.clientX - rect.left, e.clientY - rect.top);
ctx.stroke();
});
@@ -162,11 +156,13 @@ const undo = () => {
<van-cell>
<div class="sign-question">
<canvas
ref="signatureCanvas" :width="canvasWidth" :height="canvasHeight"
style="border: 1px solid #ccc; border-radius: 4px;"
ref="signatureCanvas"
:width="canvasWidth"
:height="canvasHeight"
style="border: 1px solid #ccc; border-radius: 4px"
>
</canvas>
<div class="sign-text" :class="{ 'show': showSignText }">
<div class="sign-text" :class="{ show: showSignText }">
<span @click="clearCanvas">清空</span>
<span @click="undo">撤销</span>
<span @click="togglePen">{{ isEraser ? '画笔' : '橡皮擦' }}</span>

View File

@@ -1,27 +1,39 @@
export const surveys = [
{
title: '报名签到',
icon: 'https://files.axshare.com/gsc/DR6075/de/a0/49/dea049d6ad3e4c2c80af44258c6c76d6/images/%E9%A6%96%E9%A1%B5_1/u48.png?pageId=74b3e5b2-848e-4258-8a34-9e220127c8a6'
icon: 'https://files.axshare.com/gsc/DR6075/de/a0/49/dea049d6ad3e4c2c80af44258c6c76d6/images/%E9%A6%96%E9%A1%B5_1/u48.png?pageId=74b3e5b2-848e-4258-8a34-9e220127c8a6',
scene_code: 1,
scene_code_info: 11
},
{
title: '满意度调研',
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/%E9%A6%96%E9%A1%B5_1/u27.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64'
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/%E9%A6%96%E9%A1%B5_1/u27.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64',
scene_code: 1,
scene_code_info: 16
},
{
title: '快速投票',
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/首页_1/u29.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64'
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/首页_1/u29.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64',
scene_code: 1,
scene_code_info: 13
},
{
title: '打分评估',
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/首页_1/u31.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64'
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/首页_1/u31.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64',
scene_code: 1,
scene_code_info: 14
},
{
title: 'NPS调研',
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/首页_1/u22.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64'
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/首页_1/u22.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64',
scene_code: 1,
scene_code_info: 17
},
{
title: '考评测试',
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/首页_1/u24.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64'
icon: 'https://files.axshare.com/gsc/DR6075/63/4d/77/634d77293a4d41d1b3d145974a8fb6a7/images/首页_1/u24.png?pageId=5cc10b9f-56eb-48dc-943a-bfe7afb18a64',
scene_code: 1,
scene_code_info: 15
},
{
title: '表单收集',

View File

@@ -1,12 +1,70 @@
<script setup lang="ts">
// import { ref } from 'vue';
import { consoleSurveys } from '@/api/home/index.js';
import { snQuestions, questionDetails } from '@/api/design/index.js';
import { surveys } from './Hooks/useRequestHooks';
import { useRouter } from 'vue-router';
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
// 获取 Store 实例
const counterStore = useCounterStore();
const store = storeToRefs(counterStore);
const router = useRouter();
// const surveys = ref([]);
//
// getQuestionList({}).then((res) => {
// console.log(res.data.data);
// surveys.value = res.data.data;
// });
// console.log(surveys);
const createdQuestion = (item) => {
const query = {
group_id: 0,
project_name: `${item.title}问卷 `,
remarks: '',
scene_code: item.scene_code,
scene_code_info: item.scene_code_info,
tags: ''
};
consoleSurveys(query).then((res) => {
if (res.data) {
snQuestions({ sn: res.data.data.sn }).then((ques) => {
if (ques.data) {
ques.data.data.survey.introduction = `<p>为优化活动服务品质,烦请完成问卷,感谢配合!您的反馈至关重要!(此提示语为默认提示语,您可选择自行输入本问卷的提示语)</p>`;
store.questionsInfo.value = ques.data.data;
questionDetails({
sn: res.data.data.sn,
introduction: ques.data.data.survey.introduction,
title: ques.data.data.survey.title
}).then(() => {
router.push({
path: '/create',
query: {
sn: res.data.data.sn
}
});
});
}
});
}
});
};
</script>
<template>
<van-cell style="position: relative; z-index: 1">
<div style="text-align: left">新建问卷</div>
<van-row>
<van-col v-for="survey in surveys" :key="survey.title" span="6" class="survey">
<van-col
v-for="survey in surveys"
:key="survey.title"
span="6"
class="survey"
@click="createdQuestion(survey)"
>
<img width="45px" :src="survey.icon" alt=" " />
<span>{{ survey.title }}</span>
</van-col>

View File

@@ -8,10 +8,21 @@
<van-cell-group inset class="result-cell">
<div>
<div>请输入问卷标题</div>
<div>
为优化活动服务品质烦请完成问卷感谢配合您的反馈至关重要 (
提示语为默认提示语您可选择自行输入本问卷的提示语
<!--问卷标题-->
<contenteditable
v-model="questionInfo.survey.title"
:active="true"
@blur="saveTitle"
></contenteditable>
</div>
<div>
<!-- 问卷标注-->
<contenteditable
v-model="questionInfo.survey.introduction"
:active="true"
@blur="saveTitle"
></contenteditable>
</div>
<button @click="show = true">添加题目</button>
@@ -20,36 +31,43 @@
<div class="ques">
<!-- 题目-->
<Design @get-active-question="getActiveQuestion"></Design>
<van-button @click="show = true">添加题目</van-button>
<Design :active-id="activeId" @get-active-question="getActiveQuestion"></Design>
<!-- <van-button @click="show = true">添加题目</van-button>-->
<!-- 弹出的新增题目弹窗-->
<van-popup v-model:show="show" round position="bottom" :style="{ height: '50%' }">
<van-popup
v-model:show="show"
round
:closeable="true"
position="bottom"
:style="{ maxHeight: '50%' }"
title="添加题目"
>
<van-row class="ques_title">
<van-col :span="6">添加题目</van-col>
<van-col :span="6" :offset="12" @click="show = false">
<van-icon name="close" />
</van-col>
</van-row>
<ul>
<li v-for="item in quesList" :key="item.type" @click="questionEvent(item)">
<div>
<van-icon :name="item.icon" size="20" />
<p>{{ item.name }}</p>
</div>
</li>
</ul>
<van-grid :gutter="10" class="ques_list">
<van-grid-item
v-for="item in quesList"
:key="item.type"
:icon="item.icon"
icon-color="#70b936"
:text="item.name"
@click="questionEvent(item)"
>
<template #icon>
<span class="mobilefont grid-icon" v-html="item.icon"></span>
</template>
</van-grid-item>
</van-grid>
</van-popup>
</div>
<van-cell-group inset class="thanks-cell">
<div>您已完成本次调研感谢您的参与</div>
</van-cell-group>
<!-- 底部功能性按钮 -->
<div class="survey-action">
<div class="survey-action_setting">
<van-icon name="setting" size="18" />
<span>设置</span>
<div class="survey-action_setting" @click="openSettingAction">
<van-icon name="setting" size="18" class="grid-icon" />
<span>投放设置</span>
</div>
<div class="survey-action_btn">
<van-button size="small">预览</van-button>
@@ -58,29 +76,272 @@
</div>
</div>
</div>
<!-- 投放设置-->
<van-action-sheet v-model:show="showSetting" title="">
<template #description>
<div class="flex flex-start">设置</div>
</template>
<van-cell-group :border="false" class="ml10">
<van-cell title="每页一题" :border="false" label-align="left">
<template #right-icon>
<van-switch
v-model="questionInfo.survey.is_page_one_question"
class="option-action-sheet-switch"
size="0.5rem"
:active-value="1"
:inactive-value="0"
></van-switch>
</template>
</van-cell>
<van-divider></van-divider>
<van-cell title="投放数量" :border="false" label-align="left">
<template #right-icon>
<van-switch
v-model="questionInfo.survey.is_publish_number"
class="option-action-sheet-switch"
size="0.5rem"
:active-value="1"
:inactive-value="0"
></van-switch>
</template>
</van-cell>
<van-cell-group
v-if="questionInfo.survey.is_publish_number === 1"
class="child-group"
:border="false"
>
<van-field label="投放数量最大为" input-align="right">
<template #right-icon> </template>
</van-field>
</van-cell-group>
<van-divider></van-divider>
<van-cell title="有效期" :border="false" label-align="left">
<template #right-icon>
<van-switch
v-model="questionInfo.survey.is_time"
class="option-action-sheet-switch"
size="0.5rem"
:active-value="1"
:inactive-value="0"
></van-switch>
</template>
</van-cell>
<!--有效期-->
<van-cell-group v-if="questionInfo.survey.is_time === 1" class="child-group" :border="false">
<van-field
v-model="questionInfo.survey.startTime"
is-link
label="起始时间"
input-align="right"
readonly
@click="showTimePicker('start', questionInfo.survey.startTime)"
>
</van-field>
<van-field
v-model="questionInfo.survey.endTime"
is-link
label="截至时间"
input-align="right"
readonly
@click="showTimePicker('end', questionInfo.survey.endTime)"
>
</van-field>
</van-cell-group>
<van-cell title="断点续答" :border="false" label-align="left">
<template #right-icon>
<van-switch
v-model="questionInfo.survey.is_breakpoint"
class="option-action-sheet-switch"
size="0.5rem"
:active-value="1"
:inactive-value="0"
></van-switch>
</template>
</van-cell>
<van-cell
title="企微身份获取(开启后仅支持私有化企业环境作答)"
:border="false"
label-align="left"
>
<template #right-icon>
<van-switch
v-model="questionInfo.survey.wework_status"
class="option-action-sheet-switch"
size="0.5rem"
:active-value="1"
:inactive-value="0"
></van-switch>
</template>
</van-cell>
<van-divider></van-divider>
<van-cell title="IP答题次数限制" :border="false" label-align="left">
<template #right-icon>
<van-switch
v-model="questionInfo.survey.is_ip_number"
class="option-action-sheet-switch"
size="0.5rem"
:active-value="1"
:inactive-value="0"
></van-switch>
</template>
</van-cell>
<van-cell-group v-if="questionInfo.survey.is_ip_number === 1" class="child-group">
<van-field
v-model="questionInfo.survey.is_number"
label="同一个IP地址只能作答"
:border="false"
input-align="right"
>
<template #right-icon> </template>
</van-field>
</van-cell-group>
<van-cell title="设备答题次数限制" :border="false" label-align="left">
<template #right-icon>
<van-switch
v-model="questionInfo.survey.is_browser_number"
class="option-action-sheet-switch"
size="0.5rem"
:active-value="1"
:inactive-value="0"
></van-switch>
</template>
</van-cell>
<van-cell-group
v-if="questionInfo.survey.is_browser_number === 1"
class="child-group"
:border="false"
>
<van-field
v-model="questionInfo.survey.browser_number"
label="同一个浏览器只能作答"
:border="false"
input-align="right"
>
<template #right-icon> </template>
</van-field>
</van-cell-group>
<van-divider></van-divider>
<van-field
v-model="endText"
label="结束语"
:border="false"
readonly
label-align="left"
input-align="right"
is-link
@click="openEndTextModel"
>
</van-field>
</van-cell-group>
</van-action-sheet>
<!--时间选择-->
<van-popup v-model:show="timePickerModel" position="bottom" round>
<YLPicker
title=""
:showPicker="timePickerModel"
:values="currentDate"
format="YYYY-MM-DD HH:mm:ss"
:units="['年', '月', '日', '时', '分', '秒']"
@cancel="timePickerModel = false"
@confirm="onConfirmDate"
></YLPicker>
</van-popup>
<!-- 结束语选择-->
<van-popup v-model:show="textModel" position="bottom" round>
<van-picker :columns="columns" @confirm="onConfirm"></van-picker>
</van-popup>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ref, computed, onMounted } from 'vue';
import { questionDetails, snQuestions } from '@/api/design/index';
import Design from '@/views/Design/Index.vue';
import { useCounterStore } from '@/stores/counter';
import { storeToRefs } from 'pinia';
import { v4 as uuidv4 } from 'uuid';
import { radio, checkbox, completion, rate, martrixQuestion } from '@/utils/importJsons';
import {
radio,
checkbox,
completion,
rate,
martrixQuestion,
fileUpload,
textWithImages,
signQuestion,
nps
} from '@/utils/importJsons';
import { useRoute } from 'vue-router';
import YLPicker from '@/components/YLPicker.vue';
// 获取 Store 实例
const counterStore = useCounterStore();
const store = storeToRefs(counterStore);
const chooseQuestionId = ref('');
const questionInfo = ref(store.questionsInfo.value);
const activeQuestionIndex = ref(-1);
const currentDate = ref();
const currentType = ref();
const route = useRoute();
const surveyTitle = route.meta.title as string;
const show = ref(false);
const textModel = ref(false);
const activeId = ref(0);
const showSetting = ref(false);
const timePickerModel = ref(false);
// picker 结束语选择器
const columns = ref([
{
text: '成功完成',
value: '1'
},
{
text: '题前终止',
value: '2'
},
{
text: '配额超限',
value: '3'
}
]);
const endText = ref('');
const onConfirm = (ev) => {
endText.value = columns.value[ev.selectedValues[0] - 1].text;
textModel.value = false;
};
const openSettingAction = () => {
showSetting.value = true;
};
const showTimePicker = (type, value) => {
timePickerModel.value = true;
currentType.value = type;
currentDate.value = value;
};
const openEndTextModel = () => {
textModel.value = true;
};
const onConfirmDate = (e) => {
if (currentType.value === 'start') {
questionInfo.value.survey.startTime = e;
} else {
questionInfo.value.survey.endTime = e;
}
timePickerModel.value = false;
};
// 获取选中的题目
const getActiveQuestion = (activeQues) => {
@@ -93,23 +354,31 @@ const getActiveQuestion = (activeQues) => {
});
};
const saveTitle = () => {
questionDetails({
sn: route.query.sn,
title: questionInfo.value.survey.title,
introduction: questionInfo.value.survey.introduction
});
};
const quesList = ref([
{
icon: 'location-o',
icon: '&#xe75b;',
name: '单选题',
question_type: '1',
json: radio
},
{
icon: 'like-o',
icon: '&#xe6c3;',
name: '多选题',
question_type: '2',
question_type: 2,
json: checkbox
},
{
icon: 'star-o',
icon: '&#xe6fd;',
name: '填空题',
question_type: '3',
question_type: 4,
json: completion
},
// {
@@ -119,76 +388,62 @@ const quesList = ref([
// json: rate
// },
{
icon: 'cart-o',
icon: '&#xe641;',
name: '数值打分',
question_type: '4',
question_type: 5,
json: rate
},
{
icon: 'comment-o',
icon: '&#x13c7f;',
name: '矩阵单选',
question_type: '8',
question_type: 9,
json: martrixQuestion
},
{
icon: 'bag-o',
icon: '&#xe818;',
name: '矩阵多选',
question_type: '9',
question_type: 10,
json: martrixQuestion
},
{
icon: 'gift-o',
icon: '&#xe62e;',
name: '矩阵填空',
question_type: '10',
question_type: 8,
json: martrixQuestion
},
{
icon: 'bag-o',
icon: '&#xe631;',
name: '文件上传',
question_type: '9'
question_type: 18,
json: fileUpload
},
{
icon: 'bag-o',
icon: '&#xe62c;',
name: '图文说明',
question_type: 6,
json: textWithImages
},
{
icon: '&#xe661;',
name: '签名',
question_type: 22,
json: signQuestion
},
{
icon: '&#xe6b0;',
name: 'NPS',
question_type: '106'
question_type: 106,
json: nps
}
]);
const questionEvent = (item) => {
let questionJson = {};
// switch (item.question_type) {
// // 单选 多选
// case '1':
// case '2':
//
//
// break;
// // 填空
// case '3':
// questionJson = JSON.parse(
// JSON.stringify({
// ...item.json,
// id: uuidv4(),
// question_index: questionInfo.value.survey.last_question_index + 1
// })
// );
// break;
// // 图形打分
// case '4':
// questionJson = JSON.parse(
// JSON.stringify({
// ...item.json,
// id: uuidv4(),
// question_index: questionInfo.value.survey.last_question_index + 1
// })
// );
// break
// }
const id = uuidv4();
questionJson = JSON.parse(
JSON.stringify({
...item.json,
id: uuidv4(),
id,
question_type: Number(item.question_type),
question_index: questionInfo.value.survey.last_question_index + 1,
options:
@@ -213,6 +468,8 @@ const questionEvent = (item) => {
}
// 更新题目索引
questionInfo.value.survey.last_question_index += 1;
activeId.value = id;
show.value = false;
};
const init = () => {
@@ -220,6 +477,21 @@ const init = () => {
show.value = true;
};
defineExpose({ init });
// 获取题目象棋
const getQuestionDetail = () => {
return snQuestions({ sn: route.query.sn }).then((res) => {
if (res.data) {
counterStore.setQuestionInfo(res.data.data);
return res.data.data; // 返回数据以便在onMounted中使用
}
});
};
const questionInfo = computed(() => store.questionsInfo.value);
onMounted(async() => {
await getQuestionDetail(); // 等待接口返回数据
});
</script>
<style scoped lang="scss">
@@ -257,35 +529,42 @@ defineExpose({ init });
}
.thanks-cell {
display: flex;
align-items: center;
justify-content: center;
height: 70px;
//display: flex;
//align-items: center;
//justify-content: center;
//height: 70px;
margin: 10px 5px;
}
.grid-icon {
color: #70b936;
font-size: 25px;
}
.ques {
.ques_title {
margin: 20px 0 10px;
margin: 20px 0 10px 20px;
font-weight: bold;
font-size: 16px;
}
ul {
display: flex;
flex-wrap: wrap;
justify-content: center;
padding: 10px;
li {
width: 80px;
margin: 5px;
padding: 10px;
background-color: #f7fbf5;
font-weight: bold;
font-size: 14px;
.ques_list {
margin-bottom: 10px;
}
}
.child-group {
& ::v-deep .van-field__label {
width: 140px;
color: #bfbfbf;
font-size: 12px;
}
& ::v-deep .van-cell__title {
width: 140px;
color: #bfbfbf;
font-size: 12px;
}
}
.survey-action {
@@ -294,9 +573,11 @@ defineExpose({ init });
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
justify-content: space-between;
width: 100%;
height: 50px;
//margin: 0 10px;
background-color: white;
.survey-action_setting {
@@ -304,14 +585,20 @@ defineExpose({ init });
flex-direction: column;
align-items: center;
justify-content: center;
width: 10%;
margin-left: 10px;
//width: 10%;
}
.survey-action_btn {
display: flex;
flex-direction: row;
justify-content: space-around;
width: 60%;
justify-content: space-between;
margin-right: 10px;
& .van-button + .van-button {
margin-left: 10px;
}
}
}
</style>

View File

@@ -1,32 +1,40 @@
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
// import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'; // 从 vite 导入 loadEnv
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'node:url';
import vueJsx from '@vitejs/plugin-vue-jsx';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';
import postCssPxToRem from 'postcss-pxtorem'
// https://vitejs.dev/config/
export default defineConfig({
import postCssPxToRem from 'postcss-pxtorem';
export default defineConfig(({ mode }) => {
// 接收 mode 参数
// 正确加载环境变量
const env = loadEnv(mode, process.cwd());
// 从 env 对象中获取变量
const proxyUrl = env.VITE_APP_BASEURL;
const proxyUrlDelivery = env.VITE_APP_DELIVERY_BASEURL;
return {
// 必须 return 配置对象
server: {
host: '0.0.0.0', // 监听所有网络接口
port: 3000, // 你也可以指定端口
host: '0.0.0.0',
port: 3000,
proxy: {
'/api': {
target: 'http://yls-api-uat.dctest.digitalyili.com/',
'/backend-api': {
target: proxyUrl,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
pathRewrite: {
'^/backend-api': '' // 路径重写
},
// '/api': {
// target: 'http://yls-api-uat.dctest.digitalyili.com/',
// changeOrigin: true,
// pathRewrite: {
// '^/api': 'http://yls-api-uat.dctest.digitalyili.com/', // 路径重写
// },
// cookieDomainRewrite: 'localhost',
// }
// bypass: (req) => req.headers.accept?.indexOf('html') !== -1, // 跳过 HTML 请求
cookieDomainRewrite: 'localhost'
},
'/request-java': {
target: `${proxyUrlDelivery}/api`,
changeOrigin: true,
pathRewrite: { '^/request-java': '' }
}
}
},
css: {
@@ -34,29 +42,24 @@ export default defineConfig({
plugins: [
postCssPxToRem({
rootValue: 37.5,
propList: ['*'],
propList: ['*']
})
]
},
preprocessorOptions: {
scss: {
api: 'modern-compiler', // or 'modern'
}
scss: { api: 'modern-compiler' }
}
},
plugins: [
vue(),
vueJsx(),
AutoImport({
resolvers: [VantResolver()],
}),
Components({
resolvers: [VantResolver()],
}),
AutoImport({ resolvers: [VantResolver()] }),
Components({ resolvers: [VantResolver()] })
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
};
});