Merge branch 'feature/feature-20250331-h5' of https://e.coding.yili.com/yldc/ylst/ylst-survey-h5 into feature/feature-20250331-h5
4
auto-imports.d.ts
vendored
@@ -5,6 +5,4 @@
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
|
||||
}
|
||||
declare global {}
|
||||
|
||||
6
components.d.ts
vendored
@@ -8,9 +8,15 @@ export {}
|
||||
declare module 'vue' {
|
||||
export interface GlobalComponents {
|
||||
Contenteditable: typeof import('./src/components/contenteditable.vue')['default']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElInput: typeof import('element-plus/es')['ElInput']
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSpace: typeof import('element-plus/es')['ElSpace']
|
||||
ElText: typeof import('element-plus/es')['ElText']
|
||||
RichText: typeof import('./src/components/RichText.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"
|
||||
/>
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
|
||||
9
package-lock.json
generated
@@ -21,6 +21,7 @@
|
||||
"vconsole": "^3.15.1",
|
||||
"vite-plugin-vue": "^0.0.1",
|
||||
"vue": "^3.4.29",
|
||||
"vue-icons-plus": "^0.1.8",
|
||||
"vue-router": "^4.3.3",
|
||||
"vuex": "^4.1.0"
|
||||
},
|
||||
@@ -10955,6 +10956,14 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-icons-plus": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmmirror.com/vue-icons-plus/-/vue-icons-plus-0.1.8.tgz",
|
||||
"integrity": "sha512-Xc4hDsD/oP9waSUf44nSaFBhUPo+QkpKclx0S7//5BRACpXymctbit02epek0VRW6nb81pR486XmxPP/ofm2yQ==",
|
||||
"peerDependencies": {
|
||||
"vue": ">=2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vue-router": {
|
||||
"version": "4.5.0",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -4,7 +4,7 @@ import { onMounted } from 'vue';
|
||||
import appBridge from '@/assets/js/appBridge';
|
||||
import utils from '@/assets/js/common';
|
||||
|
||||
onMounted(async() => {
|
||||
onMounted(async () => {
|
||||
if (utils.getParameter('digitalYiliToken')) {
|
||||
// 隐藏/显示 header
|
||||
appBridge.setHeaderShown(false);
|
||||
|
||||
147
src/api/common.js
Normal file
@@ -0,0 +1,147 @@
|
||||
import request from '@/utils/request';
|
||||
import createCOS from '@/utils/txyunCosUpLoad.js';
|
||||
import { uploadDocumentFile } from './upload/index.js';
|
||||
import router from '../router/index';
|
||||
import { computed } from 'vue';
|
||||
import { shrinkImage } from 'shrinkpng';
|
||||
const sn = computed(() => router.currentRoute.value.query.sn || 'default');
|
||||
export default class CommonApi {
|
||||
static uploadFileWidthSn(file, sn) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('sn', sn || '');
|
||||
|
||||
return request({
|
||||
url: '/console/upload_file',
|
||||
method: 'post',
|
||||
data: formData
|
||||
});
|
||||
}
|
||||
static uploadFileWidthSnForAnswer(file, sn) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
return request({
|
||||
url: `/answer/upload_file/${sn}`,
|
||||
method: 'post',
|
||||
data: formData
|
||||
});
|
||||
}
|
||||
/** OssInfo */
|
||||
static getOssInfo() {
|
||||
return request({
|
||||
url: '/console/files/credentials'
|
||||
});
|
||||
}
|
||||
static async cosUpload3DCompress(file, quality = 20) {
|
||||
const _file = await shrinkImage(file, { quality });
|
||||
return await this.cosUpload3D(_file);
|
||||
}
|
||||
static async cosUpload3D(file, defaultName = '') {
|
||||
let name = defaultName?.replace?.(/[\s+]/g, '_') || '';
|
||||
|
||||
const getRandomFileName = () => {
|
||||
return `3D/upload/${new Date().getTime()}_${Math.floor(Math.random() * 1000)}`;
|
||||
};
|
||||
|
||||
name = name || getRandomFileName();
|
||||
const { data } = await CommonApi.getOssInfo();
|
||||
const param = {
|
||||
region: data.Region,
|
||||
host: data.Host,
|
||||
sessionToken: data.sessionToken,
|
||||
tmpSecretId: data.tmpSecretId,
|
||||
tmpSecretKey: data.tmpSecretKey,
|
||||
bucket: data.Bucket,
|
||||
startTime: data.startTime,
|
||||
expiredTime: data.expiredTime,
|
||||
name: `${data.prefix}/${name}`,
|
||||
file
|
||||
};
|
||||
|
||||
try {
|
||||
const location = await createCOS(param);
|
||||
return {
|
||||
url: `https://${location}`,
|
||||
name
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
url: '',
|
||||
name: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 上传文件
|
||||
* @param {文件file} file
|
||||
* @returns {url:"",name:'fileName'}
|
||||
*/
|
||||
static async cosUpload(file, fileName) {
|
||||
let name = fileName?.replace?.(/[\s+]/g, '_') || '';
|
||||
|
||||
const getRandomFileName = (name) => {
|
||||
return `survey/${sn.value}/${new Date().getTime()}-${Math.floor(Math.random() * 1000)}-${name}`;
|
||||
};
|
||||
/* eslint-disable no-useless-escape */
|
||||
const reg = /\\|\/|\?|\?|\*|"|“|”|'|‘|’|<|>|{|}|\[|\]|\【|\】|:|:|、|\^|\$|!|~|`|\s|\+/g;
|
||||
name =
|
||||
name ||
|
||||
getRandomFileName(file?.name?.replace(reg, '') ?? '' ?? `${new Date().getTime()}.png`);
|
||||
const res = await CommonApi.getOssInfo();
|
||||
/* eslint-enable no-useless-escape */
|
||||
|
||||
const { data } = res.data;
|
||||
const param = {
|
||||
region: data.Region,
|
||||
host: data.Host,
|
||||
sessionToken: data.sessionToken,
|
||||
tmpSecretId: data.tmpSecretId,
|
||||
tmpSecretKey: data.tmpSecretKey,
|
||||
bucket: data.Bucket,
|
||||
startTime: data.startTime,
|
||||
expiredTime: data.expiredTime,
|
||||
name: `${data.prefix}/${name}`,
|
||||
file
|
||||
};
|
||||
try {
|
||||
const location = await createCOS(param);
|
||||
return {
|
||||
url: `https://${location}`,
|
||||
name
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
url: '',
|
||||
name: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 上传文件到阿里云和文档库
|
||||
* @param {文件file} file
|
||||
* @param {问卷id} sn
|
||||
* @param {1=问卷设计,2=数据分析,3=问卷预览,4=数据明细,5=图表分析,6=高级分析,7=市场模拟} importType
|
||||
* @returns {url:"",name:'fileName'}
|
||||
*/
|
||||
static async docuHousecosUpload(file, sn, importType, startFileName = 'survey') {
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
const reg = /\\|\/|\?|?|\*|"|“|”|'|‘|’|<|>|{|}|\[|\]|【|】|:|:|、|\^|\$|!|~|`|\s|\+/g;
|
||||
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
const fileName = `${startFileName}/${new Date().getTime()}_${Math.floor(Math.random() * 1000)}_${
|
||||
file?.name?.replace(reg, '') ?? '' ?? `${new Date().getTime()}.png`
|
||||
}`;
|
||||
try {
|
||||
const result = await this.cosUpload(file, fileName);
|
||||
uploadDocumentFile({
|
||||
url: JSON.stringify([{ name: file.name, url: result.url, size: file.size }]),
|
||||
parent_sn: sn?.value || sn,
|
||||
import_type: importType || 1
|
||||
});
|
||||
return result;
|
||||
} catch (error) {
|
||||
// error
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
// 投放问卷
|
||||
export function publishSurvey(data) {
|
||||
return request({
|
||||
url: `/console/survey_publishes`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
// 问卷详情
|
||||
export function getSurveyInfo(sn) {
|
||||
return request({
|
||||
url: `/console/surveys/${sn}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 查看二维码
|
||||
export function getQrcode(sn) {
|
||||
return request({
|
||||
url: `/console/survey_publishes/${sn}/qrcode`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
// 获取问卷测试是否弹层
|
||||
export function getCheckSurvey(sn) {
|
||||
return request({
|
||||
url: `/console/check_survey_test/${sn}`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/* 获取问题列表 */
|
||||
export function getQuestionList(params, code) {
|
||||
return request({
|
||||
headers: {
|
||||
'survey-invite-code': code || ''
|
||||
},
|
||||
url: `/console/surveys/${params}/questions`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
@@ -32,3 +32,14 @@ export function getCheckSurvey(sn) {
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
/* 获取问题列表 */
|
||||
export function getQuestionList(params, code) {
|
||||
return request({
|
||||
headers: {
|
||||
'survey-invite-code': code || ''
|
||||
},
|
||||
url: `/console/surveys/${params}/questions`,
|
||||
method: 'get'
|
||||
});
|
||||
}
|
||||
|
||||
8
src/api/upload/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import request from '@/utils/request.js';
|
||||
export function uploadDocumentFile(data) {
|
||||
return request({
|
||||
url: `/console/document_import`,
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--primary-color : rgb(111,185, 55);
|
||||
--primary-color: rgb(111, 185, 55);
|
||||
--vt-c-white: #fff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
@@ -16,6 +16,7 @@
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
--status-bar-height: 20px;
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
@@ -51,7 +52,7 @@
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
background: var(--color-background);
|
||||
background: #f2f2f2;
|
||||
color: var(--color-text);
|
||||
font-size: 15px;
|
||||
font-family:
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.van-nav-bar {
|
||||
padding-top: calc(var(--status-bar-height) + 10px) !important;
|
||||
// height: calc(46px + var(--status-bar-height)) !important;
|
||||
}
|
||||
.van-cell {
|
||||
padding: 8px !important;
|
||||
}
|
||||
@@ -32,6 +36,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.van-switch--on {
|
||||
background: $theme-color;
|
||||
}
|
||||
|
||||
.van-toast {
|
||||
padding: 10px 16px !important;
|
||||
border-radius: 5px !important;
|
||||
@@ -47,3 +55,51 @@
|
||||
background-color: rgba(0, 0, 0, 0.7) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.contenteditable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contenteditable-input {
|
||||
width: 100%;
|
||||
padding: 5px 8px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: #fafbfc;
|
||||
outline: 1px solid #f4f4f4;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.contenteditable-label {
|
||||
width: 100%;
|
||||
|
||||
//outline: 1px solid #ccc;
|
||||
//border-bottom: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.contenteditable-question-title {
|
||||
& .van-cell__title {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
bottom: -2px;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
border-bottom: 0.0267rem solid #ebedf0;
|
||||
pointer-events: none;
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
|
||||
&:focus-within::after {
|
||||
border-bottom-color: $theme-color;
|
||||
}
|
||||
|
||||
& .contenteditable-label {
|
||||
& :focus {
|
||||
//color: green;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
src/assets/img/back.png
Normal file
|
After Width: | Height: | Size: 439 B |
BIN
src/assets/img/empty.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
src/assets/img/publish/copy_icon.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
src/assets/img/publish/download_icon.png
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
src/assets/img/publish/share_icon.png
Normal file
|
After Width: | Height: | Size: 7.6 KiB |
@@ -53,12 +53,14 @@ export default {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
window.ReactNativeWebView.postMessage(JSON.stringify({
|
||||
window.ReactNativeWebView.postMessage(
|
||||
JSON.stringify({
|
||||
type: 'back',
|
||||
data: {
|
||||
force: false // 不强制返回,让原生端判断是否可以返回
|
||||
}
|
||||
}));
|
||||
})
|
||||
);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
@@ -138,20 +140,9 @@ export default {
|
||||
* @returns {Boolean} 操作是否成功
|
||||
*/
|
||||
takeOverAndroidBack() {
|
||||
if (!this.isInReactNative()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
window.ReactNativeWebView.postMessage(JSON.stringify({
|
||||
type: 'takeOverAndroidBack',
|
||||
data: {
|
||||
enabled: true
|
||||
}
|
||||
}));
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return this.postMessage({
|
||||
type: 'takeOverAndroidBack'
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -307,5 +298,24 @@ export default {
|
||||
type: 'newAuthToken',
|
||||
cbId
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @desc H5历史记录
|
||||
*/
|
||||
routerCanGoBack(router) {
|
||||
const position = router.options.history.state?.position;
|
||||
return typeof position === 'number' && position > 1;
|
||||
},
|
||||
/**
|
||||
* @desc 当前系统h5返回
|
||||
* @returns {Boolean} 操作是否成功
|
||||
*/
|
||||
async h5RouterBack(router) {
|
||||
const canGoBack = this.routerCanGoBack(router);
|
||||
if (canGoBack) {
|
||||
router.go(-1);
|
||||
} else {
|
||||
this.navigateBack();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -170,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);
|
||||
@@ -194,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);
|
||||
}
|
||||
|
||||
// 渲染全部列
|
||||
|
||||
@@ -1,26 +1,47 @@
|
||||
<template>
|
||||
<div ref="editor" :contenteditable="active" class="van-field" v-html="modelValue"></div>
|
||||
<div class="flex contenteditable align-center space-between" :class="className">
|
||||
<p
|
||||
:id="'editor' + id"
|
||||
ref="editor"
|
||||
:contenteditable="active"
|
||||
class="van-field contenteditable-content"
|
||||
@focus="onFocus"
|
||||
v-html="modelValue"
|
||||
></p>
|
||||
<div class="right-icon ml10">
|
||||
<slot name="right-icon"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="showAction && active" ref="editorAction" class="editor-action">
|
||||
<button v-for="item in actions" :key="item.name" @click="funEvent(item, $event)">
|
||||
{{ item.label }}
|
||||
<van-icon class-prefix="mobilefont" :name="item.icon"></van-icon>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { defineEmits, ref, onMounted, watch } from 'vue';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import CommonApi from '@/api/common.js';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const id = ref(uuidv4());
|
||||
|
||||
const editor = ref(null);
|
||||
const editorAction = ref(null);
|
||||
const emit = defineEmits(['update:modelValue', 'blur']);
|
||||
@@ -28,8 +49,9 @@ const save = (e) => {
|
||||
emit('update:modelValue', e.innerHTML);
|
||||
};
|
||||
|
||||
const savedRange = ref(null);
|
||||
|
||||
const functions = {
|
||||
// todo 点击按钮之后 如何判断 才能让按钮再次点击 不消失 获取重新聚焦 再次选中文本?
|
||||
boldModern: () => {
|
||||
document.execCommand('bold', true, null);
|
||||
},
|
||||
@@ -38,13 +60,45 @@ const functions = {
|
||||
},
|
||||
italic: () => {
|
||||
document.execCommand('italic', false, null);
|
||||
},
|
||||
|
||||
uploadImage: async() => {
|
||||
// 保存当前光标位置
|
||||
savedRange.value = saveSelection();
|
||||
|
||||
const fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
|
||||
fileInput.click();
|
||||
|
||||
fileInput.onchange = async(e) => {
|
||||
const [file] = e.target.files;
|
||||
if (!file) return;
|
||||
if (file.size > 2 * 1024 * 1024) {
|
||||
console.error('文件大小不能超过2M');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await CommonApi.cosUpload(file);
|
||||
const img = document.createElement('img');
|
||||
img.src = data.url;
|
||||
img.onload = () => {
|
||||
const scale = img.naturalHeight / 50;
|
||||
const width = (img.naturalWidth / scale).toFixed(0);
|
||||
const height = 50;
|
||||
const maxWidth = img.naturalWidth;
|
||||
const maxHeight = img.naturalHeight;
|
||||
const style = `style="width:${width}px;height:${height}px;max-width:${maxWidth}px;max-height:${maxHeight}px"`;
|
||||
insertImageAtCaret(`<img src=${data.url} ${style}/>`);
|
||||
|
||||
// 恢复光标位置
|
||||
restoreSelection(savedRange.value);
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const funEvent = (item) => {
|
||||
// 保持焦点在编辑器
|
||||
const selection = window.getSelection();
|
||||
selection.getRangeAt(0);
|
||||
functions[item.fun]();
|
||||
};
|
||||
|
||||
@@ -67,21 +121,22 @@ const actions = [
|
||||
{
|
||||
label: '加粗',
|
||||
fun: 'boldModern',
|
||||
icon: ''
|
||||
icon: 'jiacu'
|
||||
},
|
||||
{
|
||||
label: '下划线',
|
||||
fun: 'underLine',
|
||||
icon: ''
|
||||
icon: 'xiahuaxian'
|
||||
},
|
||||
{
|
||||
label: '图片上传',
|
||||
fun: 'uploadImage',
|
||||
icon: ''
|
||||
icon: 'tupian'
|
||||
},
|
||||
{
|
||||
label: '倾斜',
|
||||
fun: 'italic'
|
||||
fun: 'italic',
|
||||
icon: 'qingxie'
|
||||
}
|
||||
];
|
||||
const checkContains = (element, target) => {
|
||||
@@ -97,8 +152,6 @@ onMounted(() => {
|
||||
showAction.value = true;
|
||||
});
|
||||
|
||||
// 如果点击了 editor 与 editorAction 其他地方就触发保存
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!editor.value || !editorAction.value) return;
|
||||
|
||||
@@ -112,22 +165,69 @@ onMounted(() => {
|
||||
emit('blur', editor.value);
|
||||
}
|
||||
});
|
||||
// editor.value.addEventListener('blur', () => {
|
||||
// setTimeout(() => {
|
||||
// showAction.value = false;
|
||||
// });
|
||||
// });
|
||||
|
||||
document.addEventListener('resize', () => {
|
||||
showAction.value = false;
|
||||
});
|
||||
});
|
||||
|
||||
// onBeforeUnmount(() => {
|
||||
// editor.value.removeEventListener('resize', handleResize);
|
||||
// });
|
||||
const onFocus = (e) => {
|
||||
// 阻止
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
console.log('Editor focused');
|
||||
};
|
||||
|
||||
// 保存当前光标位置
|
||||
const saveSelection = () => {
|
||||
const selection = window.getSelection();
|
||||
if (selection.rangeCount === 0) return null;
|
||||
|
||||
return selection.getRangeAt(0);
|
||||
};
|
||||
|
||||
// 恢复光标位置
|
||||
const restoreSelection = (range) => {
|
||||
if (!range) return;
|
||||
|
||||
const selection = window.getSelection();
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
};
|
||||
|
||||
// 在光标位置插入图片
|
||||
const insertImageAtCaret = (html) => {
|
||||
const selection = window.getSelection();
|
||||
if (selection.rangeCount === 0) return;
|
||||
|
||||
const range = selection.getRangeAt(0);
|
||||
range.deleteContents();
|
||||
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
const frag = document.createDocumentFragment();
|
||||
for (let child = div.firstChild; child; child = div.firstChild) {
|
||||
frag.appendChild(child);
|
||||
}
|
||||
|
||||
range.insertNode(frag);
|
||||
range.setStartAfter(frag);
|
||||
range.collapse(true);
|
||||
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.contenteditable-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
color: #c4c9d4;
|
||||
}
|
||||
|
||||
.editor-action {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
@@ -143,7 +243,12 @@ onMounted(() => {
|
||||
& button {
|
||||
border: none;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button + button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -9,20 +9,12 @@
|
||||
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 {
|
||||
font-style: normal;
|
||||
font-size: 160px;
|
||||
font-family: 'iconfont logo';
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -37,8 +29,8 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
color: #666;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
@@ -46,17 +38,17 @@
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
cursor: pointer;
|
||||
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;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs .active {
|
||||
@@ -70,25 +62,25 @@
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 30px 100px;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
overflow: hidden;
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
margin-bottom: 30px;
|
||||
color: #333;
|
||||
line-height: 1;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
color: #333;
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
@@ -96,25 +88,25 @@
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
overflow: auto;
|
||||
margin: 10px 0;
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
overflow: hidden;
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-right: 20px;
|
||||
margin-bottom: 10px;
|
||||
list-style: none !important;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
@@ -125,10 +117,10 @@
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
font-size: 42px;
|
||||
line-height: 100px;
|
||||
-webkit-transition:
|
||||
font-size 0.25s linear,
|
||||
width 0.25s linear;
|
||||
@@ -145,18 +137,15 @@
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentcolor;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
@@ -181,10 +170,10 @@
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
margin-bottom: 24px;
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
@@ -192,10 +181,10 @@
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
clear: both;
|
||||
margin: 1.6em 0 0.6em;
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
@@ -223,11 +212,11 @@
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
clear: both;
|
||||
height: 1px;
|
||||
margin: 16px 0;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
@@ -270,8 +259,8 @@
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
border-radius: 3px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
@@ -280,24 +269,24 @@
|
||||
}
|
||||
|
||||
.markdown > table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
border: 1px solid #e9e9e9;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
empty-cells: show;
|
||||
}
|
||||
|
||||
.markdown > table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.markdown > table th,
|
||||
.markdown > table td {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@@ -306,11 +295,11 @@
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
margin: 1em 0;
|
||||
padding-left: 0.8em;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
color: #999;
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
@@ -318,9 +307,9 @@
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
margin-left: 8px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
@@ -333,8 +322,8 @@
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
display: inline-block;
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown > br,
|
||||
@@ -344,10 +333,10 @@
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
background: white;
|
||||
color: #333;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
@@ -383,7 +372,7 @@
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333;
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
@@ -396,13 +385,13 @@
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
background-color: #eaffea;
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
background-color: #ffecec;
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
@@ -410,10 +399,8 @@
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
|
||||
/* 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)
|
||||
@@ -421,19 +408,21 @@ https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javasc
|
||||
*/
|
||||
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;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-wrap: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
@@ -444,16 +433,16 @@ 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;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*='language-']::selection,
|
||||
pre[class*='language-'] ::selection,
|
||||
code[class*='language-']::selection,
|
||||
code[class*='language-'] ::selection {
|
||||
background: #b3d4fc;
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
@@ -465,9 +454,9 @@ code[class*='language-'] ::selection {
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*='language-'] {
|
||||
overflow: auto;
|
||||
margin: 0.5em 0;
|
||||
padding: 1em;
|
||||
margin: 0.5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*='language-'],
|
||||
@@ -521,8 +510,8 @@ pre[class*='language-'] {
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
background: hsla(0deg, 0%, 100%, 0.5);
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, 0.5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
|
||||
837
src/fonts/demo_index.html
Normal file
@@ -0,0 +1,837 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>iconfont Demo</title>
|
||||
<link
|
||||
rel="shortcut icon"
|
||||
href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"
|
||||
type="image/x-icon"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/svg+xml"
|
||||
href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css" />
|
||||
<link rel="stylesheet" href="demo.css" />
|
||||
<link rel="stylesheet" href="iconfont.css" />
|
||||
<script src="iconfont.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||
<!-- 代码高亮 -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||
<style>
|
||||
.main .logo {
|
||||
margin-top: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main .logo .sub-title {
|
||||
margin-left: 0.5em;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
background: linear-gradient(-45deg, #3967ff, #b500fe);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="logo">
|
||||
<a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
|
||||
<img
|
||||
width="200"
|
||||
src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg"
|
||||
/>
|
||||
</a>
|
||||
</h1>
|
||||
<div class="nav-tabs">
|
||||
<ul id="tabs" class="dib-box">
|
||||
<li class="dib active"><span>Unicode</span></li>
|
||||
<li class="dib"><span>Font class</span></li>
|
||||
<li class="dib"><span>Symbol</span></li>
|
||||
</ul>
|
||||
|
||||
<a
|
||||
href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4841764"
|
||||
target="_blank"
|
||||
class="nav-more"
|
||||
>查看项目</a
|
||||
>
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="content unicode" style="display: block">
|
||||
<ul class="icon_lists dib-box">
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">橡皮,擦除,橡皮擦</div>
|
||||
<div class="code-name">&#xea15;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">画笔</div>
|
||||
<div class="code-name">&#xe61f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">撤销</div>
|
||||
<div class="code-name">&#xe6e2;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">上传</div>
|
||||
<div class="code-name">&#xe613;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">清空</div>
|
||||
<div class="code-name">&#xe6dc;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">blod</div>
|
||||
<div class="code-name">&#xe71d;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">qingxie</div>
|
||||
<div class="code-name">&#xe71e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">undline</div>
|
||||
<div class="code-name">&#xe720;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">image</div>
|
||||
<div class="code-name">&#xe730;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">del</div>
|
||||
<div class="code-name">&#xe637;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">more</div>
|
||||
<div class="code-name">&#xe600;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">delete</div>
|
||||
<div class="code-name">&#xe66d;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">del</div>
|
||||
<div class="code-name">&#xe6f5;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">option</div>
|
||||
<div class="code-name">&#xe6ff;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">setting</div>
|
||||
<div class="code-name">&#xe633;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">add</div>
|
||||
<div class="code-name">&#xe686;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">radio_box</div>
|
||||
<div class="code-name">&#xe75b;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">矩阵打分</div>
|
||||
<div class="code-name">&#xe641;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">checkbox-checked</div>
|
||||
<div class="code-name">&#xe6c3;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">nps</div>
|
||||
<div class="code-name">&#xe6b0;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">input</div>
|
||||
<div class="code-name">&#xe6fd;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">矩阵填空</div>
|
||||
<div class="code-name">&#xe62e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">文件上传</div>
|
||||
<div class="code-name">&#xe631;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">签名</div>
|
||||
<div class="code-name">&#xe661;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">图文</div>
|
||||
<div class="code-name">&#xe62c;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">矩阵多选</div>
|
||||
<div class="code-name">&#xe818;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont">𓱿</span>
|
||||
<div class="name">矩阵单选</div>
|
||||
<div class="code-name">&#x13c7f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">edit-2</div>
|
||||
<div class="code-name">&#xe630;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">copy</div>
|
||||
<div class="code-name">&#xe632;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">delete</div>
|
||||
<div class="code-name">&#xe63f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont"></span>
|
||||
<div class="name">sort</div>
|
||||
<div class="code-name">&#xe6a0;</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="unicode-">Unicode 引用</h2>
|
||||
<hr />
|
||||
|
||||
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||
<ul>
|
||||
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>
|
||||
注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol
|
||||
引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)
|
||||
</p>
|
||||
</blockquote>
|
||||
<p>Unicode 使用步骤如下:</p>
|
||||
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'mobilefont';
|
||||
src: url('iconfont.woff2?t=1742102742566') format('woff2'),
|
||||
url('iconfont.woff?t=1742102742566') format('woff'),
|
||||
url('iconfont.ttf?t=1742102742566') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
<pre><code class="language-css"
|
||||
>.mobilefont {
|
||||
font-family: "mobilefont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||
<pre>
|
||||
<code class="language-html"
|
||||
><span class="mobilefont">&#x33;</span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>
|
||||
"mobilefont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-rubber"></span>
|
||||
<div class="name">橡皮,擦除,橡皮擦</div>
|
||||
<div class="code-name">.mobilefont-rubber</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-huabi"></span>
|
||||
<div class="name">画笔</div>
|
||||
<div class="code-name">.mobilefont-huabi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-chexiao"></span>
|
||||
<div class="name">撤销</div>
|
||||
<div class="code-name">.mobilefont-chexiao</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-shangchuan"></span>
|
||||
<div class="name">上传</div>
|
||||
<div class="code-name">.mobilefont-shangchuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-qingkong"></span>
|
||||
<div class="name">清空</div>
|
||||
<div class="code-name">.mobilefont-qingkong</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-jiacu"></span>
|
||||
<div class="name">blod</div>
|
||||
<div class="code-name">.mobilefont-jiacu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-qingxie"></span>
|
||||
<div class="name">qingxie</div>
|
||||
<div class="code-name">.mobilefont-qingxie</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-xiahuaxian"></span>
|
||||
<div class="name">undline</div>
|
||||
<div class="code-name">.mobilefont-xiahuaxian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-tupian"></span>
|
||||
<div class="name">image</div>
|
||||
<div class="code-name">.mobilefont-tupian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-del1"></span>
|
||||
<div class="name">del</div>
|
||||
<div class="code-name">.mobilefont-del1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-gengduo"></span>
|
||||
<div class="name">more</div>
|
||||
<div class="code-name">.mobilefont-gengduo</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-delete1"></span>
|
||||
<div class="name">delete</div>
|
||||
<div class="code-name">.mobilefont-delete1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-del"></span>
|
||||
<div class="name">del</div>
|
||||
<div class="code-name">.mobilefont-del</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-option"></span>
|
||||
<div class="name">option</div>
|
||||
<div class="code-name">.mobilefont-option</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-setting"></span>
|
||||
<div class="name">setting</div>
|
||||
<div class="code-name">.mobilefont-setting</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-add"></span>
|
||||
<div class="name">add</div>
|
||||
<div class="code-name">.mobilefont-add</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-radiobox"></span>
|
||||
<div class="name">radio_box</div>
|
||||
<div class="code-name">.mobilefont-radiobox</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-juzhendafen"></span>
|
||||
<div class="name">矩阵打分</div>
|
||||
<div class="code-name">.mobilefont-juzhendafen</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-checkbox-checked"></span>
|
||||
<div class="name">checkbox-checked</div>
|
||||
<div class="code-name">.mobilefont-checkbox-checked</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-nps"></span>
|
||||
<div class="name">nps</div>
|
||||
<div class="code-name">.mobilefont-nps</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-input"></span>
|
||||
<div class="name">input</div>
|
||||
<div class="code-name">.mobilefont-input</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-juzhentiankong"></span>
|
||||
<div class="name">矩阵填空</div>
|
||||
<div class="code-name">.mobilefont-juzhentiankong</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-wenjianshangchuan"></span>
|
||||
<div class="name">文件上传</div>
|
||||
<div class="code-name">.mobilefont-wenjianshangchuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-qianming"></span>
|
||||
<div class="name">签名</div>
|
||||
<div class="code-name">.mobilefont-qianming</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-tuwen"></span>
|
||||
<div class="name">图文</div>
|
||||
<div class="code-name">.mobilefont-tuwen</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-juzhenduoxuan"></span>
|
||||
<div class="name">矩阵多选</div>
|
||||
<div class="code-name">.mobilefont-juzhenduoxuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-juzhendanxuan"></span>
|
||||
<div class="name">矩阵单选</div>
|
||||
<div class="code-name">.mobilefont-juzhendanxuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-edit2"></span>
|
||||
<div class="name">edit-2</div>
|
||||
<div class="code-name">.mobilefont-edit2</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-copy"></span>
|
||||
<div class="name">copy</div>
|
||||
<div class="code-name">.mobilefont-copy</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-delete"></span>
|
||||
<div class="name">delete</div>
|
||||
<div class="code-name">.mobilefont-delete</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon mobilefont mobilefont-sort"></span>
|
||||
<div class="name">sort</div>
|
||||
<div class="code-name">.mobilefont-sort</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="font-class-">font-class 引用</h2>
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode
|
||||
书写不直观,语意不明确的问题。
|
||||
</p>
|
||||
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||
<ul>
|
||||
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
|
||||
<li>
|
||||
因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode
|
||||
引用。
|
||||
</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||
</code></pre>
|
||||
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><span class="mobilefont mobilefont-xxx"></span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>
|
||||
" mobilefont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-rubber"></use>
|
||||
</svg>
|
||||
<div class="name">橡皮,擦除,橡皮擦</div>
|
||||
<div class="code-name">#mobilefont-rubber</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-huabi"></use>
|
||||
</svg>
|
||||
<div class="name">画笔</div>
|
||||
<div class="code-name">#mobilefont-huabi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-chexiao"></use>
|
||||
</svg>
|
||||
<div class="name">撤销</div>
|
||||
<div class="code-name">#mobilefont-chexiao</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-shangchuan"></use>
|
||||
</svg>
|
||||
<div class="name">上传</div>
|
||||
<div class="code-name">#mobilefont-shangchuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-qingkong"></use>
|
||||
</svg>
|
||||
<div class="name">清空</div>
|
||||
<div class="code-name">#mobilefont-qingkong</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-jiacu"></use>
|
||||
</svg>
|
||||
<div class="name">blod</div>
|
||||
<div class="code-name">#mobilefont-jiacu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-qingxie"></use>
|
||||
</svg>
|
||||
<div class="name">qingxie</div>
|
||||
<div class="code-name">#mobilefont-qingxie</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-xiahuaxian"></use>
|
||||
</svg>
|
||||
<div class="name">undline</div>
|
||||
<div class="code-name">#mobilefont-xiahuaxian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-tupian"></use>
|
||||
</svg>
|
||||
<div class="name">image</div>
|
||||
<div class="code-name">#mobilefont-tupian</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-del1"></use>
|
||||
</svg>
|
||||
<div class="name">del</div>
|
||||
<div class="code-name">#mobilefont-del1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-gengduo"></use>
|
||||
</svg>
|
||||
<div class="name">more</div>
|
||||
<div class="code-name">#mobilefont-gengduo</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-delete1"></use>
|
||||
</svg>
|
||||
<div class="name">delete</div>
|
||||
<div class="code-name">#mobilefont-delete1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-del"></use>
|
||||
</svg>
|
||||
<div class="name">del</div>
|
||||
<div class="code-name">#mobilefont-del</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-option"></use>
|
||||
</svg>
|
||||
<div class="name">option</div>
|
||||
<div class="code-name">#mobilefont-option</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-setting"></use>
|
||||
</svg>
|
||||
<div class="name">setting</div>
|
||||
<div class="code-name">#mobilefont-setting</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-add"></use>
|
||||
</svg>
|
||||
<div class="name">add</div>
|
||||
<div class="code-name">#mobilefont-add</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-radiobox"></use>
|
||||
</svg>
|
||||
<div class="name">radio_box</div>
|
||||
<div class="code-name">#mobilefont-radiobox</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-juzhendafen"></use>
|
||||
</svg>
|
||||
<div class="name">矩阵打分</div>
|
||||
<div class="code-name">#mobilefont-juzhendafen</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-checkbox-checked"></use>
|
||||
</svg>
|
||||
<div class="name">checkbox-checked</div>
|
||||
<div class="code-name">#mobilefont-checkbox-checked</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-nps"></use>
|
||||
</svg>
|
||||
<div class="name">nps</div>
|
||||
<div class="code-name">#mobilefont-nps</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-input"></use>
|
||||
</svg>
|
||||
<div class="name">input</div>
|
||||
<div class="code-name">#mobilefont-input</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-juzhentiankong"></use>
|
||||
</svg>
|
||||
<div class="name">矩阵填空</div>
|
||||
<div class="code-name">#mobilefont-juzhentiankong</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-wenjianshangchuan"></use>
|
||||
</svg>
|
||||
<div class="name">文件上传</div>
|
||||
<div class="code-name">#mobilefont-wenjianshangchuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-qianming"></use>
|
||||
</svg>
|
||||
<div class="name">签名</div>
|
||||
<div class="code-name">#mobilefont-qianming</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-tuwen"></use>
|
||||
</svg>
|
||||
<div class="name">图文</div>
|
||||
<div class="code-name">#mobilefont-tuwen</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-juzhenduoxuan"></use>
|
||||
</svg>
|
||||
<div class="name">矩阵多选</div>
|
||||
<div class="code-name">#mobilefont-juzhenduoxuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-juzhendanxuan"></use>
|
||||
</svg>
|
||||
<div class="name">矩阵单选</div>
|
||||
<div class="code-name">#mobilefont-juzhendanxuan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-edit2"></use>
|
||||
</svg>
|
||||
<div class="name">edit-2</div>
|
||||
<div class="code-name">#mobilefont-edit2</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-copy"></use>
|
||||
</svg>
|
||||
<div class="name">copy</div>
|
||||
<div class="code-name">#mobilefont-copy</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-delete"></use>
|
||||
</svg>
|
||||
<div class="name">delete</div>
|
||||
<div class="code-name">#mobilefont-delete</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#mobilefont-sort"></use>
|
||||
</svg>
|
||||
<div class="name">sort</div>
|
||||
<div class="code-name">#mobilefont-sort</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="symbol-">Symbol 引用</h2>
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a
|
||||
href=""
|
||||
>文章</a
|
||||
>
|
||||
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
|
||||
</p>
|
||||
<ul>
|
||||
<li>支持多色图标了,不再受单色限制。</li>
|
||||
<li>
|
||||
通过一些技巧,支持像字体那样,通过 <code>font-size</code>,
|
||||
<code>color</code> 来调整样式。
|
||||
</li>
|
||||
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||
</code></pre>
|
||||
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||
<pre><code class="language-html"><style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xxx"></use>
|
||||
</svg>
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.tab-container .content:first').show();
|
||||
|
||||
$('#tabs li').click(function (e) {
|
||||
var tabContent = $('.tab-container .content');
|
||||
var index = $(this).index();
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
return;
|
||||
} else {
|
||||
$('#tabs li').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
|
||||
tabContent.hide().eq(index).fadeIn();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,75 +1,119 @@
|
||||
@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');
|
||||
url('iconfont.woff2?t=1742025232194') format('woff2'),
|
||||
url('iconfont.woff?t=1742025232194') format('woff'),
|
||||
url('iconfont.ttf?t=1742025232194') format('truetype');
|
||||
}
|
||||
|
||||
.mobilefont {
|
||||
font-style: normal;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
font-family: mobilefont !important;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-radiobox::before {
|
||||
.mobilefont-jiacu::before {
|
||||
content: '\e71d';
|
||||
}
|
||||
|
||||
.mobilefont-qingxie::before {
|
||||
content: '\e71e';
|
||||
}
|
||||
|
||||
.mobilefont-xiahuaxian::before {
|
||||
content: '\e720';
|
||||
}
|
||||
|
||||
.mobilefont-tupian::before {
|
||||
content: '\e730';
|
||||
}
|
||||
|
||||
.mobilefont-del1::before {
|
||||
content: '\e637';
|
||||
}
|
||||
|
||||
.mobilefont-gengduo::before {
|
||||
content: '\e600';
|
||||
}
|
||||
|
||||
.mobilefont-delete1::before {
|
||||
content: '\e66d';
|
||||
}
|
||||
|
||||
.mobilefont-del::before {
|
||||
content: '\e6f5';
|
||||
}
|
||||
|
||||
.mobilefont-option::before {
|
||||
content: '\e6ff';
|
||||
}
|
||||
|
||||
.mobilefont-setting::before {
|
||||
content: '\e633';
|
||||
}
|
||||
|
||||
.mobilefont-add::before {
|
||||
content: '\e686';
|
||||
}
|
||||
|
||||
.mobilefont-radiobox::before {
|
||||
content: '\e75b';
|
||||
}
|
||||
|
||||
.icon-juzhendafen::before {
|
||||
.mobilefont-juzhendafen::before {
|
||||
content: '\e641';
|
||||
}
|
||||
|
||||
.icon-checkbox-checked::before {
|
||||
.mobilefont-checkbox-checked::before {
|
||||
content: '\e6c3';
|
||||
}
|
||||
|
||||
.icon-nps::before {
|
||||
.mobilefont-nps::before {
|
||||
content: '\e6b0';
|
||||
}
|
||||
|
||||
.icon-input::before {
|
||||
.mobilefont-input::before {
|
||||
content: '\e6fd';
|
||||
}
|
||||
|
||||
.icon-juzhentiankong::before {
|
||||
.mobilefont-juzhentiankong::before {
|
||||
content: '\e62e';
|
||||
}
|
||||
|
||||
.icon-wenjianshangchuan::before {
|
||||
.mobilefont-wenjianshangchuan::before {
|
||||
content: '\e631';
|
||||
}
|
||||
|
||||
.icon-qianming::before {
|
||||
.mobilefont-qianming::before {
|
||||
content: '\e661';
|
||||
}
|
||||
|
||||
.icon-tuwen::before {
|
||||
.mobilefont-tuwen::before {
|
||||
content: '\e62c';
|
||||
}
|
||||
|
||||
.icon-juzhenduoxuan::before {
|
||||
.mobilefont-juzhenduoxuan::before {
|
||||
content: '\e818';
|
||||
}
|
||||
|
||||
.icon-juzhendanxuan::before {
|
||||
.mobilefont-juzhendanxuan::before {
|
||||
content: '\13c7f';
|
||||
}
|
||||
|
||||
.icon-edit2::before {
|
||||
.mobilefont-edit2::before {
|
||||
content: '\e630';
|
||||
}
|
||||
|
||||
.icon-copy::before {
|
||||
.mobilefont-copy::before {
|
||||
content: '\e632';
|
||||
}
|
||||
|
||||
.icon-delete::before {
|
||||
.mobilefont-delete::before {
|
||||
content: '\e63f';
|
||||
}
|
||||
|
||||
.icon-sort::before {
|
||||
.mobilefont-sort::before {
|
||||
content: '\e6a0';
|
||||
}
|
||||
|
||||
@@ -1,10 +1,59 @@
|
||||
{
|
||||
"id": "4841764",
|
||||
"name": "yl",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"font_family": "mobilefont",
|
||||
"css_prefix_text": "mobilefont-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "6135228",
|
||||
"name": "del",
|
||||
"font_class": "del1",
|
||||
"unicode": "e637",
|
||||
"unicode_decimal": 58935
|
||||
},
|
||||
{
|
||||
"icon_id": "159969",
|
||||
"name": "more",
|
||||
"font_class": "gengduo",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "627633",
|
||||
"name": "delete",
|
||||
"font_class": "delete1",
|
||||
"unicode": "e66d",
|
||||
"unicode_decimal": 58989
|
||||
},
|
||||
{
|
||||
"icon_id": "669644",
|
||||
"name": "del",
|
||||
"font_class": "del",
|
||||
"unicode": "e6f5",
|
||||
"unicode_decimal": 59125
|
||||
},
|
||||
{
|
||||
"icon_id": "17956030",
|
||||
"name": "option",
|
||||
"font_class": "option",
|
||||
"unicode": "e6ff",
|
||||
"unicode_decimal": 59135
|
||||
},
|
||||
{
|
||||
"icon_id": "18261691",
|
||||
"name": "setting",
|
||||
"font_class": "setting",
|
||||
"unicode": "e633",
|
||||
"unicode_decimal": 58931
|
||||
},
|
||||
{
|
||||
"icon_id": "22873715",
|
||||
"name": "add",
|
||||
"font_class": "add",
|
||||
"unicode": "e686",
|
||||
"unicode_decimal": 59014
|
||||
},
|
||||
{
|
||||
"icon_id": "240132",
|
||||
"name": "radio_box",
|
||||
|
||||
32
src/layouts/config3d.constant.js
Normal file
@@ -0,0 +1,32 @@
|
||||
// 问题类型
|
||||
export const QUESTION_TYPE = {
|
||||
// 单选
|
||||
CHOICE: 1,
|
||||
|
||||
// 多选
|
||||
CHOICE_MULT: 2,
|
||||
|
||||
// 输入
|
||||
INPUT_MULT: 4,
|
||||
|
||||
// 图文说明
|
||||
IMG_TEXT_EXPLAIN: 6,
|
||||
|
||||
// 热区-开关
|
||||
HOTAREA: 25,
|
||||
|
||||
// 热区-喜欢/不喜欢
|
||||
HOTAREA_2: 26,
|
||||
|
||||
// 矩阵-填空
|
||||
MATRIX_INPUT: 8,
|
||||
|
||||
// 矩阵-单选
|
||||
MATRIX_RADIO: 9,
|
||||
|
||||
contains(item) {
|
||||
return Object.keys(this).some((key) => this[key] === item);
|
||||
}
|
||||
};
|
||||
|
||||
export default {};
|
||||
@@ -2,125 +2,49 @@
|
||||
<div class="common-layout">
|
||||
<!-- title 标题和搜索栏 -->
|
||||
<header class="header">
|
||||
<van-nav-bar
|
||||
class="header-nav"
|
||||
:title="$route.meta.title"
|
||||
left-arrow
|
||||
@click-left="$router.go(-1)"
|
||||
/>
|
||||
<van-search
|
||||
shape="round"
|
||||
class="header-search"
|
||||
placeholder="请输入搜索关键词"
|
||||
background="#6fb937"
|
||||
/>
|
||||
<van-nav-bar :title="$route.meta.title" left-arrow safe-area-inset-top @click-left="goBack">
|
||||
<template #left>
|
||||
<img src="@/assets/img/back.png" alt="" class="back-icon" />
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
</header>
|
||||
<!-- content -->
|
||||
<RouterView />
|
||||
<!-- tabbar -->
|
||||
<van-tabbar v-model="active">
|
||||
<van-tabbar-item
|
||||
v-for="tab in table"
|
||||
:key="tab.title"
|
||||
:name="tab.title"
|
||||
@click="tabPath(tab.path)"
|
||||
>
|
||||
<!-- <van-tabbar-item v-for="tab in table" :key="tab.title" :name="tab.title" :to="tab.path"> -->
|
||||
<span>{{ tab.title }}</span>
|
||||
<!-- <template #icon="props"> -->
|
||||
<template #icon>
|
||||
<!-- <img :src="props.active ? icon.active : icon.inactive" /> -->
|
||||
<img :src="tab.icon" />
|
||||
</template>
|
||||
</van-tabbar-item>
|
||||
</van-tabbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { RouterView, useRouter } from 'vue-router';
|
||||
import { ref } from 'vue';
|
||||
// import utils from '@/assets/js/common';
|
||||
// import { getUserInfo } from '@/api/common/index.js';
|
||||
// import { showFailToast } from 'vant';
|
||||
import appBridge from '@/assets/js/appBridge';
|
||||
const router = useRouter();
|
||||
|
||||
const active = ref();
|
||||
const table = [
|
||||
{
|
||||
title: '首页',
|
||||
path: '/',
|
||||
icon: 'https://files.axshare.com/gsc/DR6075/de/a0/49/dea049d6ad3e4c2c80af44258c6c76d6/images/%E9%A6%96%E9%A1%B5_1/u18.png?pageId=74b3e5b2-848e-4258-8a34-9e220127c8a6'
|
||||
},
|
||||
{
|
||||
title: '问卷',
|
||||
path: '/survey',
|
||||
icon: 'https://files.axshare.com/gsc/DR6075/de/a0/49/dea049d6ad3e4c2c80af44258c6c76d6/images/%E9%A6%96%E9%A1%B5_1/u21.png?pageId=74b3e5b2-848e-4258-8a34-9e220127c8a6'
|
||||
},
|
||||
{
|
||||
title: '新建问卷',
|
||||
path: '/create',
|
||||
icon: 'https://files.axshare.com/gsc/DR6075/de/a0/49/dea049d6ad3e4c2c80af44258c6c76d6/images/首页_1/u15.png?pageId=74b3e5b2-848e-4258-8a34-9e220127c8a6'
|
||||
},
|
||||
{
|
||||
title: '模板',
|
||||
path: '/market',
|
||||
icon: 'https://files.axshare.com/gsc/DR6075/de/a0/49/dea049d6ad3e4c2c80af44258c6c76d6/images/%E9%A6%96%E9%A1%B5_1/u24.png?pageId=74b3e5b2-848e-4258-8a34-9e220127c8a6'
|
||||
}
|
||||
];
|
||||
function tabPath(path) {
|
||||
router.push({
|
||||
path
|
||||
});
|
||||
// if (utils.getParameter('digitalYiliToken')) {
|
||||
// const appToken = utils.getParameter('digitalYiliToken');
|
||||
// getUserInfo(appToken)
|
||||
// .then((res) => {
|
||||
// if (res.data) {
|
||||
// utils.setSessionStorage('userInfo', res.data.data);
|
||||
// router.push({
|
||||
// path
|
||||
// });
|
||||
// } else {
|
||||
// showFailToast(
|
||||
// error.response.data?.message || error.data?.message || error.message || '服务器错误'
|
||||
// );
|
||||
// }
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// showFailToast(error?.response?.data?.message || error?.message || '服务器错误');
|
||||
// });
|
||||
// } else {
|
||||
// router.push({
|
||||
// path
|
||||
// });
|
||||
// }
|
||||
function goBack() {
|
||||
appBridge.h5RouterBack(router);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.common-layout {
|
||||
min-height: calc(100vh);
|
||||
//min-height: calc(100vh);
|
||||
background-color: white;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header {
|
||||
.header-nav {
|
||||
background: var(--primary-color) !important;
|
||||
}
|
||||
|
||||
.header-search {
|
||||
padding: 13px;
|
||||
|
||||
//.input-class {
|
||||
//}
|
||||
}
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
background-color: #a5d380;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
463
src/layouts/logic.js
Normal file
@@ -0,0 +1,463 @@
|
||||
/**
|
||||
* 问卷设计中涉及到的一些 【逻辑】 中公共方法
|
||||
*/
|
||||
import config from '@/config';
|
||||
import { showDialog } from 'vant';
|
||||
const advancedQuesTypeList = [
|
||||
{
|
||||
name: 'Maxdiff',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 105
|
||||
},
|
||||
{
|
||||
name: 'CBC',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 103
|
||||
},
|
||||
{
|
||||
name: 'BPTO',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 104
|
||||
},
|
||||
{
|
||||
name: 'PSM',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 101
|
||||
},
|
||||
{
|
||||
name: 'KANO',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 102
|
||||
}
|
||||
];
|
||||
const advancedTypes = advancedQuesTypeList.map((i) => i.type); // 高级题型的 question_type
|
||||
|
||||
const performance = {
|
||||
start(label) {
|
||||
if (config.currentMode === 'dev') {
|
||||
console.time(`${label} Performance`);
|
||||
}
|
||||
},
|
||||
end(label) {
|
||||
if (config.currentMode === 'dev') {
|
||||
console.timeEnd(`${label} Performance`);
|
||||
}
|
||||
},
|
||||
log(...rest) {
|
||||
if (config.currentMode === 'dev') {
|
||||
console.log(...rest);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function isNullish(value) {
|
||||
return value === undefined || value === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改(添加、移动、删除)问卷 题目、分页,编辑(添加、删除)逻辑 时,判断循环逻辑是否满足
|
||||
* 的异步方法,感觉题多了这个方法执行会很慢。。。
|
||||
* @param rest
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
export async function loopingAvailableAsync(...rest) {
|
||||
return Promise.resolve(loopingAvailable(...rest));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改(添加、移动、删除)问卷 题目、分页,编辑(添加、删除)逻辑 时,判断循环逻辑是否满足
|
||||
* @param cycles 循环逻辑列表
|
||||
* @param questions 被操作后的问题列表
|
||||
* @param logics 逻辑列表
|
||||
* @param isPerPage 是否是每页一道题
|
||||
* @param reason 提示信息
|
||||
* @return {boolean} 是否满足循环逻辑
|
||||
*/
|
||||
export function loopingAvailable({ cycles, questions, logics, isPerPage, reason } = {}) {
|
||||
const performanceName = 'CheckLoopingAvailable';
|
||||
performance.start(performanceName);
|
||||
|
||||
let result = true;
|
||||
let prompt = reason || '';
|
||||
|
||||
if (!cycles.length) {
|
||||
// 没有配置循环逻辑
|
||||
performance.end(performanceName);
|
||||
return result;
|
||||
}
|
||||
|
||||
const quizList = generateQuestionPages({ questions, logics, isPerPage });
|
||||
const cycleList = generateCyclePages(cycles, quizList);
|
||||
const logicList = generateLogicPages(logics, quizList);
|
||||
|
||||
const lastPage = quizList.reduce((prev, curr) => Math.max(prev, curr.$page || 0), 0); // 最后一页的页码
|
||||
|
||||
// console.log('questions', questions, quizList);
|
||||
// console.log('cycles', cycles, cycleList);
|
||||
// console.log('logics', logics, logicList);
|
||||
// console.log('last page', lastPage);
|
||||
|
||||
for (let i = 0; i < cycleList.length; i += 1) {
|
||||
const { index, page, startPage, endPage } = cycleList[i] || {};
|
||||
|
||||
// 循环关联问题被删除时,才会出现这种情况
|
||||
if (!quizList.find((j) => j.question_index === index)) {
|
||||
result = false;
|
||||
prompt = '问题被循环关联,不能删除';
|
||||
break;
|
||||
}
|
||||
|
||||
if ((startPage && startPage <= page) || (endPage && endPage <= page)) {
|
||||
result = false;
|
||||
prompt = '问题被循环关联,不能位于循环题组后';
|
||||
break;
|
||||
}
|
||||
|
||||
// 循环关联问题位于自己或其它循环的循环题组分页中间
|
||||
if (page && cycleList.some((j) => j.startPage < page && page < j.endPage)) {
|
||||
result = false;
|
||||
prompt = '问题被循环关联,不能位于循环题组中';
|
||||
break;
|
||||
}
|
||||
|
||||
// 循环的循环题组,修改问题或分页的时候应该不会出现这种情况
|
||||
if (startPage && endPage && startPage > endPage) {
|
||||
result = false;
|
||||
prompt = '循环题组的起始分页应该小于结束分页';
|
||||
break;
|
||||
}
|
||||
|
||||
// 删除分页,或者删除自带分页的题,导致分页变化,校验分页是否包含了所有的循环题组分页
|
||||
if (endPage > lastPage) {
|
||||
result = false;
|
||||
prompt = '请勿在循环题组中编辑分页,请调整循环题组后再试';
|
||||
break;
|
||||
}
|
||||
|
||||
// 循环题组分页和跳转逻辑有冲突
|
||||
if (
|
||||
logicList.some((j) => {
|
||||
// 跳转到这几个的时候,循环不校验,只校验带题的
|
||||
// 正常完成、提前终止、配额超限
|
||||
if ([-1, -2, -3].includes(Math.min(...j))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isCross(j, [startPage, endPage]);
|
||||
})
|
||||
) {
|
||||
result = false;
|
||||
prompt = '循环题组分页区间与跳转逻辑冲突,请修改后再试';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!result && (reason || prompt)) {
|
||||
showDialog({
|
||||
title: '无法操作',
|
||||
width: '450px',
|
||||
message: reason || prompt
|
||||
});
|
||||
}
|
||||
|
||||
performance.end(performanceName);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 给问题列表中的问题添加,问题所在的页码字段($page)
|
||||
* 说明:
|
||||
* 1. 需要过滤掉没有题的空白页
|
||||
* 2. 高级题型的题前后需要加分页
|
||||
* 3. 添加了逻辑的基础题型前后需要加分页
|
||||
* @param questions
|
||||
* @param logics
|
||||
* @param isPerPage
|
||||
* @param addon 自定义额外操作,是一个方法
|
||||
* @return {array} 题目列表,包括题和分页
|
||||
*/
|
||||
export function generateQuestionPages({ questions, logics, isPerPage, addon }) {
|
||||
if (!questions?.length) {
|
||||
return questions || [];
|
||||
}
|
||||
|
||||
let page = 1;
|
||||
let pageObjectCount = 0; // 计数,在 questions 有几个分页的对象
|
||||
return questions.map((item, index, arr) => {
|
||||
const asBlock = [...advancedTypes, 23]; // 需要自动加分页的题型:高级题型及知情同意书
|
||||
const isPrevAdv = asBlock.includes(arr[index - 1]?.question_type); // 前一道题是高级题型
|
||||
const isAdv = asBlock.includes(item?.question_type); // 当前题是高级题型
|
||||
// const isPrevAdv = advancedTypes.includes(arr[index - 1]?.question_type); // 前一道题是高级题型
|
||||
// const isAdv = advancedTypes.includes(item?.question_type); // 当前题是高级题型
|
||||
|
||||
// 前一道题配置了跳转逻辑
|
||||
const isPrevLogic = !!logics.find(
|
||||
(logic) => logic.question_index === arr[index - 1]?.question_index
|
||||
);
|
||||
// 当前题配置了跳转逻辑
|
||||
const isLogic = !!logics.find((logic) => logic.question_index === item.question_index);
|
||||
|
||||
const isSurroundedByPage = isAdv || isLogic; // 需要在该题前后添加分页
|
||||
|
||||
const isPrevPage = !!arr[index - 1]?.page; // 前一道题是一个分页对象
|
||||
const prevIsQuestion = !arr[index - 1]?.page; // 前一道题是一道题而不是一个分页对象
|
||||
|
||||
if (!index) {
|
||||
page = 1;
|
||||
pageObjectCount = 0;
|
||||
}
|
||||
|
||||
if (item.page) {
|
||||
pageObjectCount += 1;
|
||||
if (index && prevIsQuestion && !isPrevLogic && !isPrevAdv) {
|
||||
// 过滤掉没有题的空白页
|
||||
page += 1;
|
||||
}
|
||||
} else {
|
||||
if (index && !isPrevPage && !isPrevAdv && !isPrevLogic && isSurroundedByPage) {
|
||||
page += 1;
|
||||
}
|
||||
item.$page = isPerPage ? index + 1 - pageObjectCount : page;
|
||||
if (isSurroundedByPage) {
|
||||
page += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (addon) {
|
||||
addon(item);
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化循环列表,便于后续使用
|
||||
* @param cycles
|
||||
* @param questions
|
||||
* @return {array} 格式化后的循环列表
|
||||
*/
|
||||
export function generateCyclePages(cycles, questions) {
|
||||
if (!cycles?.length) {
|
||||
return cycles || [];
|
||||
}
|
||||
|
||||
return cycles.map((cycle) => ({
|
||||
index: cycle.question_index,
|
||||
page: getPageByQuestionIndex(cycle.question_index, questions),
|
||||
startPage: cycle.first_page,
|
||||
endPage: cycle.last_page
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 逻辑关联的页码,便于后续使用
|
||||
* @param logics {*[]}
|
||||
* @param questions {*[]}
|
||||
* @param fillRangeItem {boolean} 用数字填充满 range[index] 数组
|
||||
* @return {number[][]} 逻辑关联的页码
|
||||
*/
|
||||
export function generateLogicPages(logics, questions, fillRangeItem) {
|
||||
if (!logics?.length) {
|
||||
return logics || [];
|
||||
}
|
||||
|
||||
const pages = [];
|
||||
|
||||
logics.forEach((logic) => {
|
||||
if (![0, 2].includes(logic.skip_type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let page = [
|
||||
getPageByQuestionIndex(logic.logic?.[0]?.question_index, questions),
|
||||
getPageByQuestionIndex(logic.question_index, questions),
|
||||
getPageByQuestionIndex(logic.skip_question_index, questions) || logic.skip_question_index
|
||||
];
|
||||
page = page.filter((i) => !!i);
|
||||
|
||||
if (fillRangeItem) {
|
||||
const min = Math.min(...page.filter((i) => i > 0));
|
||||
const max = Math.max(...page);
|
||||
page = generateRange(min, max);
|
||||
}
|
||||
|
||||
pages.push(page);
|
||||
});
|
||||
|
||||
return pages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过问题的 question_index 找到问题在第几页
|
||||
* @param questionIndex
|
||||
* @param questions
|
||||
* @return {number|*|undefined}
|
||||
*/
|
||||
export function getPageByQuestionIndex(questionIndex, questions) {
|
||||
return questions.find((i) => i.question_index === questionIndex)?.$page || undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个从 start 开始到 end 结束的数组
|
||||
* @param start {number}
|
||||
* @param end {number}
|
||||
* @return {number[]}
|
||||
*/
|
||||
export function generateRange(start, end) {
|
||||
if (isNullish(start) || isNullish(end)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (isNaN(Number(start)) || isNaN(Number(end))) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let i = start;
|
||||
const range = [];
|
||||
while (i <= end) {
|
||||
range.push(i);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断逻辑与循环分组,是否不合理
|
||||
* @param range1 {array} 跳转逻辑的分页数组
|
||||
* @param range2 {array} 循环分组的分页数组
|
||||
* @return {boolean} true 不合理, false 合理
|
||||
*/
|
||||
export function isCross(range1, range2) {
|
||||
if (!range1 || !range2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const parsedRange1 = range1.slice(1);
|
||||
const judge = range1[0];
|
||||
|
||||
const start1 = Math.min(...parsedRange1);
|
||||
const end1 = Math.max(...parsedRange1);
|
||||
const start2 = range2[0];
|
||||
const end2 = range2[1];
|
||||
|
||||
const isPlainSequence = parsedRange1[0] === start1; // 跳转逻辑的方向,true 为从前向后跳转
|
||||
|
||||
if (isNullish(start1) || isNullish(end1) || end1 < 0 || (isNullish(start2) && isNullish(end2))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// [judge, start1, end1]; // isPlainSequence
|
||||
// [start1, judge, end1]; // isPlainSequence || !isPlainSequence
|
||||
//
|
||||
// [start2, end2];
|
||||
|
||||
// 逻辑在循环之前
|
||||
const isLeft = isNullish(start2) ? end1 < end2 : end1 < start2;
|
||||
// 逻辑在循环之后
|
||||
const isRight = isNullish(end2)
|
||||
? start2 < start1 && start2 < judge
|
||||
: end2 < start1 && end2 < judge;
|
||||
// 不相交也不包含
|
||||
const isSibling = isLeft || isRight;
|
||||
|
||||
// 逻辑包含循环
|
||||
const contain
|
||||
= (isPlainSequence
|
||||
&& (((isNullish(start2) || isSequence(judge, start2, start1))
|
||||
&& (isNullish(end2) || isSequence(judge, end2, start1)))
|
||||
|| ((isNullish(start2) || isSequence(start1, start2, end1))
|
||||
&& (isNullish(end2) || isSequence(start1, end2, end1)))))
|
||||
|| (!isPlainSequence
|
||||
&& (judge < start1
|
||||
? ((isNullish(start2) || isSequence(judge, start2, start1))
|
||||
&& (isNullish(end2) || isSequence(judge, end2, start1)))
|
||||
|| ((isNullish(start2) || isSequence(start1, start2, end1))
|
||||
&& (isNullish(end2) || isSequence(start1, end2, end1)))
|
||||
: ((isNullish(start2) || isSequence(start1, start2, judge))
|
||||
&& (isNullish(end2) || isSequence(start1, end2, judge)))
|
||||
|| ((isNullish(start2) || isSequence(judge, start2, end1))
|
||||
&& (isNullish(end2) || isSequence(judge, end2, end1)))));
|
||||
// 循环存在封闭区间,并且循环包含逻辑
|
||||
const contained
|
||||
= !isNullish(start2)
|
||||
&& !isNullish(end2)
|
||||
// [judge, start1, end1];
|
||||
&& ((isPlainSequence && start2 <= judge && end1 <= end2)
|
||||
// [judge, start1, end1];
|
||||
// [start1, judge, end1];
|
||||
|| (!isPlainSequence && start2 <= start1 && start2 <= judge && end1 <= end2));
|
||||
// 循环不存在封闭区间
|
||||
const unCircled
|
||||
= (!isNullish(start2)
|
||||
&& isNullish(end2)
|
||||
&& ((isPlainSequence && start2 === judge) || (!isPlainSequence && judge < start1)
|
||||
? start2 === judge
|
||||
: start2 === start1))
|
||||
|| (isNullish(start2) && !isNullish(end2) && end2 === end1);
|
||||
|
||||
return !(isSibling || contain || contained || unCircled);
|
||||
}
|
||||
|
||||
function isSequence(s1, s2, s3, equal) {
|
||||
return equal ? s1 <= s2 && s2 <= s3 : s1 < s2 && s2 < s3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用保存问题接口前,检查是否有问题受到循环影响需要重新保存,如果有则重新保存一下这道题,没有则不需要特殊处理
|
||||
* bugfix for : 有循环的问卷发布后,再次编辑问卷,将循环题组内的问题移除循环题组,
|
||||
* 再次发布,后端不处理被移除的问题 title,导致该题的 title 仍保持上次发布时的值
|
||||
* 错误格式一般为:B3.1 正确格式一般为:B3
|
||||
* 导致作答出现错误,例如:引用找不到题
|
||||
* 从 store 里查出修改前和修改后的问题、分页、循环;比较修改前后问题是否被移除了某个循环题组;修改 quesSaveParam.newQuestion;
|
||||
* @param quesSaveParam 将要保存的数据,会被此方法修改的字段:quesSaveParam.newQuestion
|
||||
* @param store
|
||||
*/
|
||||
export function updateNewQuestionsByLoopingEffect(quesSaveParam, store) {
|
||||
const { questionInfoBeforeModified = {}, questionInfo = {} }
|
||||
= JSON.parse(JSON.stringify(store.state.common)) || {};
|
||||
|
||||
const oldPages = questionInfoBeforeModified.survey.pages;
|
||||
const newQuestions = questionInfo.questions;
|
||||
const newPages = questionInfo.survey.is_one_page_one_question
|
||||
? questionInfo.questions.filter((i) => i.question_index).map((i) => [i.question_index])
|
||||
: questionInfo.survey.pages;
|
||||
const cycles = questionInfo.cycle_pages || [];
|
||||
|
||||
if (!cycles.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const moveOutOfCycleQuestionIndex = [];
|
||||
const cyclePages = cycles.map((i) => [i.first_page, i.last_page]).filter((i) => i[0] && i[1]);
|
||||
cyclePages.forEach((i) => {
|
||||
const start = i[0] - 1;
|
||||
const end = i[1] - 1;
|
||||
|
||||
for (let j = start; j <= end; j += 1) {
|
||||
if (oldPages[j]?.join(',') !== newPages[j]?.join(',')) {
|
||||
oldPages[j]?.forEach((k) => {
|
||||
if (!newPages?.[j]?.includes(k)) {
|
||||
moveOutOfCycleQuestionIndex.push(k);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const movedOutOfCycleQuestions = newQuestions.filter((i) =>
|
||||
moveOutOfCycleQuestionIndex.includes(i.question_index)
|
||||
);
|
||||
|
||||
if (movedOutOfCycleQuestions.length) {
|
||||
if (!quesSaveParam.newQuestion) {
|
||||
quesSaveParam.newQuestion = [];
|
||||
}
|
||||
quesSaveParam.newQuestion.push(...movedOutOfCycleQuestions);
|
||||
}
|
||||
}
|
||||
311
src/layouts/quesInfoList.js
Normal file
@@ -0,0 +1,311 @@
|
||||
const basicQuesTypeList = [
|
||||
{
|
||||
name: '选择题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
childTypes: [1, 2]
|
||||
},
|
||||
{
|
||||
name: '级联题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
childTypes: [3]
|
||||
},
|
||||
{
|
||||
name: '填空题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
childTypes: [4]
|
||||
},
|
||||
{
|
||||
name: '打分题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 5,
|
||||
childTypes: [5]
|
||||
},
|
||||
{
|
||||
name: '矩阵题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 9,
|
||||
childTypes: [8, 9, 10, 11]
|
||||
},
|
||||
{
|
||||
name: '图片题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 13,
|
||||
childTypes: [12, 13, 14]
|
||||
},
|
||||
{
|
||||
name: '分类题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 15,
|
||||
childTypes: [15]
|
||||
},
|
||||
{
|
||||
name: '排序题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 16,
|
||||
childTypes: [16]
|
||||
},
|
||||
{
|
||||
name: '图文说明题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 6,
|
||||
childTypes: [6]
|
||||
},
|
||||
{
|
||||
name: '日期/时间',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 7,
|
||||
childTypes: [7]
|
||||
},
|
||||
{
|
||||
name: '恒定总和题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 17,
|
||||
childTypes: [17]
|
||||
},
|
||||
{
|
||||
name: '文件上传题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 18,
|
||||
childTypes: [18]
|
||||
},
|
||||
// {
|
||||
// name: "组合表格题",
|
||||
// icon: "",
|
||||
// check: false,
|
||||
// type: 24,
|
||||
// childTypes: [24],
|
||||
// },
|
||||
{
|
||||
name: '热区题',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 25,
|
||||
childTypes: [25, 56]
|
||||
},
|
||||
{
|
||||
name: 'NPS',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 106,
|
||||
childTypes: [106]
|
||||
}
|
||||
];
|
||||
const quickQuesTypeList = [
|
||||
{
|
||||
name: '姓名',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
quickType: 6
|
||||
},
|
||||
{
|
||||
name: '性别',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
quickType: 7
|
||||
},
|
||||
{
|
||||
name: '手机号',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 20
|
||||
},
|
||||
{
|
||||
name: '身份证号',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
quickType: 2
|
||||
},
|
||||
{
|
||||
name: '邮箱',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
quickType: 8
|
||||
},
|
||||
{
|
||||
name: '年龄',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 4,
|
||||
quickType: 9
|
||||
},
|
||||
{
|
||||
name: '年龄段',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
quickType: 10
|
||||
},
|
||||
{
|
||||
name: '生日',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 7,
|
||||
quickType: 11
|
||||
},
|
||||
{
|
||||
name: '学历',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
quickType: 12
|
||||
},
|
||||
{
|
||||
name: '院校',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 13
|
||||
},
|
||||
{
|
||||
name: '专业',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 14
|
||||
},
|
||||
{
|
||||
name: '行业',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 1,
|
||||
quickType: 15
|
||||
},
|
||||
{
|
||||
name: '地理位置',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 19,
|
||||
quickType: 16
|
||||
},
|
||||
{
|
||||
name: '省份',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 3
|
||||
},
|
||||
{
|
||||
name: '省市',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 4
|
||||
},
|
||||
{
|
||||
name: '省市区/县',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 3,
|
||||
quickType: 5
|
||||
},
|
||||
{
|
||||
type: 21,
|
||||
name: '密码',
|
||||
icon: '',
|
||||
check: false
|
||||
},
|
||||
{
|
||||
type: 22,
|
||||
name: '签名题',
|
||||
icon: '',
|
||||
check: false
|
||||
},
|
||||
{
|
||||
type: 23,
|
||||
name: '知情同意书',
|
||||
icon: '',
|
||||
check: false
|
||||
}
|
||||
];
|
||||
const advancedQuesTypeList = [
|
||||
{
|
||||
name: 'Maxdiff',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 105
|
||||
},
|
||||
{
|
||||
name: 'CBC',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 103
|
||||
},
|
||||
{
|
||||
name: 'BPTO',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 104
|
||||
},
|
||||
{
|
||||
name: 'PSM',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 101
|
||||
},
|
||||
{
|
||||
name: 'KANO',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 102
|
||||
}
|
||||
];
|
||||
const d3QuestypeList = [
|
||||
{
|
||||
name: 'ID test',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 200
|
||||
},
|
||||
{
|
||||
name: 'Experiment',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 201
|
||||
}
|
||||
];
|
||||
const uxSimuatorQuestypeList = [
|
||||
{
|
||||
name: 'UI',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 202
|
||||
},
|
||||
{
|
||||
name: 'Prototype',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 203
|
||||
},
|
||||
{
|
||||
name: 'UE',
|
||||
icon: '',
|
||||
check: false,
|
||||
type: 204
|
||||
}
|
||||
];
|
||||
module.exports = {
|
||||
basicQuesTypeList,
|
||||
quickQuesTypeList,
|
||||
advancedQuesTypeList,
|
||||
d3QuestypeList,
|
||||
uxSimuatorQuestypeList
|
||||
};
|
||||
@@ -1,10 +1,9 @@
|
||||
// import { getQuesByPages } from '@/views/planetDesign/Design/js/util';
|
||||
// import { A_COMMON_SET_ACTIVEQUESTION } from '@store/constance/constance.common'
|
||||
// import { createVNode } from 'vue';
|
||||
import { showConfirmDialog } from 'vant';
|
||||
import { getQuestionList, getCheckSurvey } from '@/api/publish';
|
||||
// import store from '@/store';
|
||||
// import Scroll from '@views/planetDesign/Design/js/scroll'
|
||||
import { getQuestionList, getCheckSurvey } from '@/api/survey';
|
||||
import appBridge from '@/assets/js/appBridge';
|
||||
import { QUESTION_TYPE } from '@/layouts/config3d.constant.js';
|
||||
import { loopingAvailable } from '@/layouts/logic.js';
|
||||
import { getDomText } from '@/utils/utils';
|
||||
// /**
|
||||
// * 统一的弹窗
|
||||
// * @param options
|
||||
@@ -14,30 +13,28 @@ import { getQuestionList, getCheckSurvey } from '@/api/publish';
|
||||
* @param options
|
||||
*/
|
||||
function showModal(options) {
|
||||
const confirm = (...rest) => {
|
||||
if (options.incompleteQuestionList?.length) {
|
||||
if (options.onOk) {
|
||||
options.onOk(...rest);
|
||||
}
|
||||
// const confirm = (...rest) => {
|
||||
// if (options.incompleteQuestionList?.length) {
|
||||
// if (options.onOk) {
|
||||
// options.onOk(...rest);
|
||||
// }
|
||||
// const firstQuestion = options.incompleteQuestionList[0];
|
||||
// store.commit(`common/${A_COMMON_SET_ACTIVEQUESTION}`, JSON.stringify(firstQuestion));
|
||||
// const el = document.getElementById(firstQuestion.id);
|
||||
|
||||
// new Scroll(el).animate();
|
||||
}
|
||||
};
|
||||
// }
|
||||
// };
|
||||
|
||||
showConfirmDialog({
|
||||
class: 'custom-modal custom-modal-title-notice',
|
||||
title: '提示',
|
||||
icon: null,
|
||||
...options
|
||||
})
|
||||
.then(() => {
|
||||
confirm();
|
||||
// confirm();
|
||||
})
|
||||
.catch(() => {
|
||||
/**/
|
||||
// catch();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,14 +45,14 @@ function showModal(options) {
|
||||
*/
|
||||
const canPlanetPublishPSM = function(data) {
|
||||
let isFb = true;
|
||||
let content = '';
|
||||
let message = '';
|
||||
let title = '题目设置未完成';
|
||||
const incompleteQuestionList = [];
|
||||
data.questions
|
||||
&& data.questions.forEach((s) => {
|
||||
if (s.question_type === 101 && s.config.price_gradient.length <= 0) {
|
||||
isFb = false;
|
||||
content = 'psm题目未完成设置,请设置价格区间后投放';
|
||||
message = 'psm题目未完成设置,请设置价格区间后投放';
|
||||
title = '题目设置未完成';
|
||||
incompleteQuestionList.push(s);
|
||||
}
|
||||
@@ -67,7 +64,7 @@ const canPlanetPublishPSM = function(data) {
|
||||
const titleStr = incompleteQuestionList.map((item) => item.title).join('、') || '';
|
||||
showModal({
|
||||
title,
|
||||
content: `${titleStr} ${content}`,
|
||||
message: `${titleStr} ${message}`,
|
||||
incompleteQuestionList
|
||||
});
|
||||
}
|
||||
@@ -79,7 +76,7 @@ const canPlanetPublishPSM = function(data) {
|
||||
*/
|
||||
const canPlanetPublishMxdAndHotArea = function(data) {
|
||||
let isFb = true;
|
||||
let content = '';
|
||||
let message = '';
|
||||
const qSteams = [];
|
||||
const incompleteQuestionList = [];
|
||||
let type = 0;
|
||||
@@ -88,7 +85,7 @@ const canPlanetPublishMxdAndHotArea = function(data) {
|
||||
&& data.questions.forEach((s) => {
|
||||
if (s.question_type === 105 && s.config.design_version <= 0) {
|
||||
isFb = false;
|
||||
content = 'maxdiff题目未完成设置,请生成设计后投放';
|
||||
message = 'maxdiff题目未完成设置,请生成设计后投放';
|
||||
title = '题目设置未完成';
|
||||
type = 1;
|
||||
incompleteQuestionList.push(s);
|
||||
@@ -109,11 +106,11 @@ const canPlanetPublishMxdAndHotArea = function(data) {
|
||||
if (type === 2) {
|
||||
const titleStr = qSteams.join(',');
|
||||
title = '添加选区';
|
||||
content = `${titleStr} 未添加选区,请添加选区后进行投放`;
|
||||
message = `${titleStr} 未添加选区,请添加选区后进行投放`;
|
||||
}
|
||||
showModal({
|
||||
title,
|
||||
content,
|
||||
message,
|
||||
incompleteQuestionList
|
||||
});
|
||||
}
|
||||
@@ -127,7 +124,7 @@ const canPlanetPublishMxdAndHotArea = function(data) {
|
||||
const canPlanetPublish3D = function(data) {
|
||||
{
|
||||
let canFB = true;
|
||||
let content = '';
|
||||
let message = '';
|
||||
const qSteams = [];
|
||||
let title = '';
|
||||
data.questions
|
||||
@@ -139,7 +136,7 @@ const canPlanetPublish3D = function(data) {
|
||||
qSteams.push(`(${s.title})`);
|
||||
}
|
||||
} catch (error) {
|
||||
// console.warn(error);
|
||||
// eslint-disable-next-line no-eval
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -147,10 +144,10 @@ const canPlanetPublish3D = function(data) {
|
||||
if (!canFB === true) {
|
||||
const titleStr = qSteams.join(',');
|
||||
title = '选择场景';
|
||||
content = `${titleStr} 未选择场景,请选择场景后进行投放`;
|
||||
message = `${titleStr} 未选择场景,请选择场景后进行投放`;
|
||||
showModal({
|
||||
title,
|
||||
content
|
||||
message
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -158,7 +155,7 @@ const canPlanetPublish3D = function(data) {
|
||||
|
||||
{
|
||||
let canFB = true;
|
||||
let content = '';
|
||||
let message = '';
|
||||
const qSteams = [];
|
||||
let title = '';
|
||||
data.questions
|
||||
@@ -181,15 +178,13 @@ const canPlanetPublish3D = function(data) {
|
||||
|
||||
const options = s.options.flat();
|
||||
s.associate.forEach((ass) => {
|
||||
// eslint-disable-next-line no-eval
|
||||
const question = data.questions.find(
|
||||
(q) => q.question_index === ass.question_index
|
||||
);
|
||||
if (!question) return;
|
||||
options.push(...question.options.flat());
|
||||
});
|
||||
|
||||
// options = options.filter(option => !option.is_other);
|
||||
|
||||
// 判定是否有选项未关联商品
|
||||
if (options.length > wares.length) {
|
||||
canFB = false;
|
||||
@@ -197,7 +192,7 @@ const canPlanetPublish3D = function(data) {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// console.warn(error);
|
||||
// error
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -205,10 +200,10 @@ const canPlanetPublish3D = function(data) {
|
||||
if (!canFB === true) {
|
||||
const titleStr = qSteams.join(',');
|
||||
title = '商品关联选项';
|
||||
content = `${titleStr} 仍有选项未关联商品,无法进行投放`;
|
||||
message = `${titleStr} 仍有选项未关联商品,无法进行投放`;
|
||||
showModal({
|
||||
title,
|
||||
content
|
||||
message
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -224,7 +219,7 @@ const canPlanetPublish3D = function(data) {
|
||||
const canPlanetPublishImage = function(data) {
|
||||
{
|
||||
let canFB = true;
|
||||
let content = '';
|
||||
let message = '';
|
||||
const qSteams = [];
|
||||
let title = '';
|
||||
data.questions
|
||||
@@ -236,18 +231,17 @@ const canPlanetPublishImage = function(data) {
|
||||
qSteams.push(`(${s.title})`);
|
||||
}
|
||||
} catch (error) {
|
||||
// 错误返回
|
||||
console.warn(error);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!canFB === true) {
|
||||
const titleStr = qSteams.join(',');
|
||||
title = '题目设置未完成';
|
||||
// content = `${titleStr} 未上传图片,请上传图片后进行投放`;
|
||||
content = `图片题 ${titleStr} 未上传图片,请设置后投放`;
|
||||
message = `图片题 ${titleStr} 未上传图片,请设置后投放`;
|
||||
showModal({
|
||||
title,
|
||||
content
|
||||
message
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -282,7 +276,7 @@ function canPublishMultiCompletion(data, publishType) {
|
||||
if (questions.length) {
|
||||
showModal({
|
||||
title: '题目设置未完成',
|
||||
content: `多项填空题 (${questions.join('、')}) 未设置填空,请设置后${publishStr}`
|
||||
message: `多项填空题 (${questions.join('、')}) 未设置填空,请设置后${publishStr}`
|
||||
});
|
||||
return false;
|
||||
}
|
||||
@@ -291,7 +285,7 @@ function canPublishMultiCompletion(data, publishType) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 “逻辑 -> 随机列表” 是否满足投放条件
|
||||
* 校验 "逻辑 -> 随机列表" 是否满足投放条件
|
||||
* @param data
|
||||
* @return {boolean}
|
||||
*/
|
||||
@@ -299,14 +293,15 @@ function canPublishRandom(data, publishType) {
|
||||
const publishStr = ['', '预览', '投放'][publishType] || '投放';
|
||||
|
||||
const errors = [];
|
||||
// eslint-disable-next-line no-eval
|
||||
const randomList = data?.survey?.group_pages || [];
|
||||
|
||||
randomList.forEach((random /* randomIndex */) => {
|
||||
randomList.forEach((random) => {
|
||||
const list = random.list || [];
|
||||
|
||||
// 每一个随机,至少要有两个随机题组
|
||||
if (list.length < 2) {
|
||||
errors.push({ message: `“${random.title}”需至少配置2个“随机题组”` });
|
||||
errors.push({ message: `"${random.title}"需至少配置2个"随机题组"` });
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -324,7 +319,7 @@ function canPublishRandom(data, publishType) {
|
||||
errors.push({
|
||||
message:
|
||||
field.message
|
||||
|| `请填写“${random.title}”中第${index + 1}组“随机题组”的“${field.name}”`
|
||||
|| `请填写"${random.title}"中第${index + 1}组"随机题组"的"${field.name}"`
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -334,53 +329,86 @@ function canPublishRandom(data, publishType) {
|
||||
if (errors.length) {
|
||||
showModal({
|
||||
title: '修改随机题组',
|
||||
content: `随机题组设置不完全,请前往“逻辑设置-随机列表”修改后${publishStr}`
|
||||
message: `随机题组设置不完全,请前往"逻辑设置-随机列表"修改后${publishStr}`
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据原始数据pages和问题列表quesList处理成包含分页的quesList
|
||||
* @param {Array} questions 要合并的题目
|
||||
* @param {Array} page 分页完成的题目ques_index数组
|
||||
* @param {Array} pageConfig 分页配置,由于是后添加的功能,所以只好分开传,再合到一起了
|
||||
* @returns
|
||||
*/
|
||||
function getQuesByPages(questions, page, pageConfig) {
|
||||
const oldPages = questions.filter((q) => !!q.page);
|
||||
const quesList = questions.filter((ques) => ques.id);
|
||||
const pageStr = [];
|
||||
page.forEach((x, xIndex) => {
|
||||
const temp = {
|
||||
...(oldPages[xIndex] || {}),
|
||||
page: xIndex + 1,
|
||||
total: page.length,
|
||||
first_title: '',
|
||||
last_title: '',
|
||||
...(pageConfig?.[xIndex] || {})
|
||||
};
|
||||
x.forEach((y, yIndex) => {
|
||||
if (yIndex === 0) {
|
||||
temp.first_title = quesList.find((ques) => ques.question_index === y)?.title || '';
|
||||
}
|
||||
if (yIndex === x.length - 1) {
|
||||
temp.last_title = quesList.find((ques) => ques.question_index === y)?.title || '';
|
||||
}
|
||||
});
|
||||
pageStr.push(temp);
|
||||
});
|
||||
return combineQuesAndPage(quesList, page, pageStr);
|
||||
}
|
||||
/**
|
||||
* 循环逻辑的判断
|
||||
* @param data
|
||||
* @param publishType
|
||||
*/
|
||||
// function isLoopingLogicValid(data, publishType) {
|
||||
// const publishStr = ['', '预览', '投放'][publishType] || '投放';
|
||||
//
|
||||
// if (
|
||||
// (data?.cycle_pages || []).every((i) => {
|
||||
// return (
|
||||
// i.question_index
|
||||
// && i.relation_type !== undefined
|
||||
// && i.relation_type !== null
|
||||
// && i.first_page
|
||||
// && i.last_page
|
||||
// );
|
||||
// })
|
||||
// ) {
|
||||
// return loopingAvailable({
|
||||
// cycles: data.cycle_pages || [],
|
||||
// questions: getQuesByPages(data.questions || [], data.survey.pages),
|
||||
// logics: data.logics || [],
|
||||
// isPerPage: data.survey?.is_one_page_one_question
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// showDialog({
|
||||
// class: 'custom-modal custom-modal-title-notice show-icon',
|
||||
// title: '修改循环',
|
||||
// content: `循环题组不完全,请前往循环列表修改后${publishStr}`
|
||||
// });
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
function isLoopingLogicValid(data, publishType) {
|
||||
const publishStr = ['', '预览', '投放'][publishType] || '投放';
|
||||
|
||||
if (
|
||||
(data?.cycle_pages || []).every((i) => {
|
||||
return (
|
||||
i.question_index
|
||||
&& i.relation_type !== undefined
|
||||
&& i.relation_type !== null
|
||||
&& i.first_page
|
||||
&& i.last_page
|
||||
);
|
||||
})
|
||||
) {
|
||||
return loopingAvailable({
|
||||
cycles: data.cycle_pages || [],
|
||||
questions: getQuesByPages(data.questions || [], data.survey.pages),
|
||||
logics: data.logics || [],
|
||||
isPerPage: data.survey?.is_one_page_one_question
|
||||
});
|
||||
}
|
||||
|
||||
showModal({
|
||||
title: '修改循环',
|
||||
message: `循环题组不完全,请前往循环列表修改后${publishStr}`
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 判断问卷是否可以投放(原本应该后端实现,前端实现会有并发的问题,但后端表示做不了)
|
||||
* @param sn
|
||||
* @param publishType undefined投放;null投放;0投放;1预览;2投放;3测试
|
||||
*/
|
||||
export const canPlanetPublish = async function(sn, publishType) {
|
||||
const parsedPublishType = !publishType ? 2 : publishType;
|
||||
|
||||
const num = window.location.href.indexOf('code=');
|
||||
let code;
|
||||
if (num > -1) {
|
||||
@@ -395,7 +423,7 @@ export const canPlanetPublish = async function(sn, publishType) {
|
||||
if (!canPlanetPublishImage(data)) return false;
|
||||
if (!canPublishMultiCompletion(data, parsedPublishType)) return false;
|
||||
if (!canPublishRandom(data, parsedPublishType)) return false;
|
||||
// if (!isLoopingLogicValid(data, parsedPublishType)) return false;
|
||||
if (!isLoopingLogicValid(data, parsedPublishType)) return false;
|
||||
|
||||
if (parsedPublishType === 2) {
|
||||
const qrcodeRes = await getCheckSurvey(sn);
|
||||
@@ -404,39 +432,7 @@ export const canPlanetPublish = async function(sn, publishType) {
|
||||
showConfirmDialog({
|
||||
class: 'custom-modal custom-modal-title-confirm-notice show-icon',
|
||||
title: '确认要投放这个问卷吗?',
|
||||
// content: () =>
|
||||
// createVNode('div', {}, [
|
||||
// createVNode(
|
||||
// 'div',
|
||||
// {
|
||||
// style: {
|
||||
// position: 'absolute',
|
||||
// top: '0',
|
||||
// right: '0',
|
||||
// display: 'flex',
|
||||
// justifyContent: 'center',
|
||||
// alignItems: 'center',
|
||||
// width: '56px',
|
||||
// height: '56px',
|
||||
// fontSize: '14px',
|
||||
// cursor: 'pointer'
|
||||
// },
|
||||
// onClick: () => {
|
||||
// console.log('close');
|
||||
// modal.destroy();
|
||||
// resolve(false);
|
||||
// }
|
||||
// },
|
||||
// // 使用 HTML 实体 × 来替代 ant - design 的 CloseOutlined
|
||||
// ['×']
|
||||
// ),
|
||||
// createVNode(
|
||||
// 'div',
|
||||
// {},
|
||||
// '投放前测试能帮助您确认问卷设计逻辑及作答数据是否正确,避免因问卷设计问题造成重大损失。'
|
||||
// )
|
||||
// ]),
|
||||
contentStyle: {
|
||||
messageStyle: {
|
||||
position: 'absolute',
|
||||
top: '0',
|
||||
right: '0',
|
||||
@@ -452,8 +448,7 @@ export const canPlanetPublish = async function(sn, publishType) {
|
||||
'投放前测试能帮助您确认问卷设计逻辑及作答数据是否正确,避免因问卷设计问题造成重大损失。',
|
||||
confirmButtonText: '去测试',
|
||||
onConfirm() {
|
||||
resolve(false);
|
||||
// window.open(`${location.origin}/#/answer?sn=${sn}&is_test=1`);
|
||||
appBridge.openInBrowser(`${location.origin}/#/answer?sn=${sn}&is_test=1`);
|
||||
},
|
||||
width: '640px',
|
||||
height: '364px',
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import './assets/css/main.scss';
|
||||
import 'amfe-flexible';
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
@@ -10,6 +9,8 @@ import 'vant/lib/index.css';
|
||||
import '@/style/utils.scss';
|
||||
import appBridge from '@/assets/js/appBridge';
|
||||
import VConsole from 'vconsole';
|
||||
import './assets/css/main.scss';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
if (import.meta.env.VITE_APP_ENV !== 'production') {
|
||||
@@ -32,7 +33,7 @@ router.beforeEach((to, from, next) => {
|
||||
if (to.query.digitalYiliToken) {
|
||||
utils.setSessionStorage('xToken', to.query.digitalYiliToken);
|
||||
}
|
||||
appBridge.setTitle(to.meta.title as string);
|
||||
// appBridge.setTitle(to.meta.title as string);
|
||||
// 添加 Android 返回按钮监听方法
|
||||
window.onAndroidBack = () => {
|
||||
if (routerCanGoBack()) {
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export const surveyQuestion
|
||||
= 'https://yls-api-uat.dctest.digitalyili.com/api/console/surveys/RWNK9BYp/questions';
|
||||
export const surveyQuestion =
|
||||
'https://yls-api-uat.dctest.digitalyili.com/api/console/surveys/RWNK9BYp/questions';
|
||||
|
||||
@@ -38,10 +38,10 @@ service.interceptors.request.use(
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
if (
|
||||
response.status === 200
|
||||
|| response.status === 201
|
||||
|| response.status === 202
|
||||
|| response.status === 204
|
||||
response.status === 200 ||
|
||||
response.status === 201 ||
|
||||
response.status === 202 ||
|
||||
response.status === 204
|
||||
) {
|
||||
if (response.config.method === 'put') {
|
||||
// message.success('保存中...');
|
||||
|
||||
@@ -10,14 +10,14 @@ const router = createRouter({
|
||||
component: layout,
|
||||
redirect: '/home',
|
||||
meta: {
|
||||
title: '首页'
|
||||
title: '伊调研'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
meta: {
|
||||
title: '首页'
|
||||
title: '伊调研'
|
||||
},
|
||||
component: () => import('../views/Home/Index.vue')
|
||||
},
|
||||
|
||||
@@ -44,10 +44,10 @@ service.interceptors.request.use(
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
if (
|
||||
response.status === 200
|
||||
|| response.status === 201
|
||||
|| response.status === 202
|
||||
|| response.status === 204
|
||||
response.status === 200 ||
|
||||
response.status === 201 ||
|
||||
response.status === 202 ||
|
||||
response.status === 204
|
||||
) {
|
||||
if (response.config.method === 'put') {
|
||||
// message.success('保存中...');
|
||||
|
||||
56
src/utils/txyunCosUpLoad.js
Normal file
@@ -0,0 +1,56 @@
|
||||
// const COS = require('cos-js-sdk-v5');
|
||||
import COS from 'cos-js-sdk-v5';
|
||||
/**
|
||||
*
|
||||
* @param {腾讯云的pramas} data
|
||||
* @returns
|
||||
*/
|
||||
export default function createCOS(data) {
|
||||
const {
|
||||
bucket,
|
||||
host,
|
||||
region,
|
||||
sessionToken,
|
||||
tmpSecretId,
|
||||
tmpSecretKey,
|
||||
startTime,
|
||||
expiredTime,
|
||||
name,
|
||||
file
|
||||
} = data;
|
||||
|
||||
const config = {
|
||||
Region: region,
|
||||
Host: host,
|
||||
Bucket: bucket,
|
||||
Key: name,
|
||||
Body: file,
|
||||
onProgress: (progressData) => {
|
||||
console.log(progressData);
|
||||
}
|
||||
};
|
||||
const client = new COS({
|
||||
getAuthorization: (options, callback) => {
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
callback({
|
||||
TmpSecretId: tmpSecretId,
|
||||
TmpSecretKey: tmpSecretKey,
|
||||
SecurityToken: sessionToken,
|
||||
// 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
|
||||
StartTime: startTime, // 时间戳,单位秒,如:1580000000
|
||||
ExpiredTime: expiredTime
|
||||
});
|
||||
}
|
||||
// eslint-enable standard/no-callback-literal
|
||||
});
|
||||
return new Promise((resolve, reject) => {
|
||||
client.putObject(config, (err, data) => {
|
||||
console.log('腾讯上传', err, data);
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(data.Location);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<div class="design-create">
|
||||
<draggable
|
||||
v-if="questionInfo.questions.length > 0"
|
||||
v-model:data="questionInfo.questions"
|
||||
item-key="id"
|
||||
handle=".moverQues"
|
||||
handle=".moveQuestion"
|
||||
chosenClass="chosen"
|
||||
animation="300"
|
||||
:scroll="true"
|
||||
@@ -86,7 +87,7 @@
|
||||
:active="chooseQuestionId === element.id"
|
||||
@update:element="updateElement"
|
||||
/>
|
||||
<!--图文-->
|
||||
<!--NPS-->
|
||||
<NPS
|
||||
v-if="element.question_type === 106"
|
||||
:element="computedElement(element)"
|
||||
@@ -104,8 +105,11 @@
|
||||
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>
|
||||
<div
|
||||
class="flex align-center action-item theme-color"
|
||||
@click="actionEvent(act, el)"
|
||||
>
|
||||
<van-icon :name="act.icon" class="icons"></van-icon>
|
||||
<span class="ml10">{{ act.label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -130,6 +134,9 @@
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
<div v-else>
|
||||
<slot name="empty"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
@@ -163,7 +170,7 @@ const { filterGap, activeId } = defineProps({
|
||||
default: false
|
||||
},
|
||||
activeId: {
|
||||
type: String,
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
@@ -243,7 +250,8 @@ watch(
|
||||
() => questionInfo.value.questions,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
saveQueItem(questionInfo.value.logics, newVal); // 确保保存最新的数据
|
||||
// 确保保存最新的数据
|
||||
saveQueItem(questionInfo.value.logics, newVal);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
@@ -264,7 +272,7 @@ const actionOptions = [
|
||||
actions: [
|
||||
{
|
||||
label: '添加选项',
|
||||
icon: 'add',
|
||||
icon: 'add-o',
|
||||
fun: 'radioAddOption'
|
||||
}
|
||||
]
|
||||
@@ -275,12 +283,12 @@ const actionOptions = [
|
||||
actions: [
|
||||
{
|
||||
label: '添加行标签',
|
||||
icon: 'add',
|
||||
icon: 'add-o',
|
||||
fun: 'addMatrixRowOption'
|
||||
},
|
||||
{
|
||||
label: '添加列标签',
|
||||
icon: 'add',
|
||||
icon: 'add-o',
|
||||
fun: 'addMatrixColumnOption'
|
||||
}
|
||||
]
|
||||
@@ -443,7 +451,11 @@ onMounted(() => {
|
||||
//background-color: #e9eef3;
|
||||
color: #333;
|
||||
|
||||
.slot-actions {
|
||||
::v-deep .slot-actions {
|
||||
& .action-item {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
& .action-item + .action-item {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
<template>
|
||||
<div class="option-action">
|
||||
<template v-for="(item, index) in data" :key="index">
|
||||
<draggable
|
||||
v-model:data="data"
|
||||
item-key="option_index"
|
||||
:handle="handle"
|
||||
chosenClass="chosen"
|
||||
animation="300"
|
||||
:scroll="true"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div class="flex align-center option-action-container">
|
||||
<slot name="item" :element="item" :index="index"></slot>
|
||||
<slot name="item" :element="element" :index="index"></slot>
|
||||
<span v-if="active" class="flex">
|
||||
<van-icon name="close" @click="deleteOption(index)"></van-icon>
|
||||
<van-icon name="setting-o" @click="openMoveModel(item, index)"></van-icon>
|
||||
<van-icon name="more-o" @click="openOptionActionModel(item, index)"></van-icon>
|
||||
<!--<van-icon class-prefix="mobilefont"
|
||||
name="setting "
|
||||
@click="openMoveModel(element, index)
|
||||
"/>-->
|
||||
<van-icon
|
||||
class-prefix="mobilefont"
|
||||
name="gengduo "
|
||||
@click="openOptionActionModel(element, index)"
|
||||
/>
|
||||
<van-icon class-prefix="mobilefont" name="del1 " @click="deleteOption(index)" />
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
<!-- 操作项弹窗-->
|
||||
<van-action-sheet v-model:show="show">
|
||||
@@ -66,15 +82,8 @@
|
||||
import CheckboxAction from './components/OptionItemAction/CheckboxAction.vue';
|
||||
import { ref } from 'vue';
|
||||
import { showConfirmDialog } from 'vant';
|
||||
import Draggable from '@/views/Design/components/Draggable.vue';
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
stem: ''
|
||||
};
|
||||
}
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -84,9 +93,17 @@ const props = defineProps({
|
||||
default: () => {
|
||||
// 空
|
||||
}
|
||||
},
|
||||
handle: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const data = defineModel('data', {
|
||||
type: Array,
|
||||
default: () => []
|
||||
});
|
||||
const actions = [
|
||||
{ name: '上移选项', action: 'up' },
|
||||
{ name: '下移选项', action: 'down' }
|
||||
@@ -109,11 +126,11 @@ const openOptionActionModel = (item, index) => {
|
||||
activeOption.value = item;
|
||||
activeIndex.value = index;
|
||||
};
|
||||
const openMoveModel = (item, index) => {
|
||||
moveShow.value = true;
|
||||
activeOption.value = item;
|
||||
activeIndex.value = index;
|
||||
};
|
||||
// const openMoveModel = (item, index) => {
|
||||
// moveShow.value = true;
|
||||
// activeOption.value = item;
|
||||
// activeIndex.value = index;
|
||||
// };
|
||||
// 上下移动
|
||||
const optionMove = (action) => {
|
||||
switch (action.action) {
|
||||
@@ -162,11 +179,23 @@ const deleteOption = (index) => {
|
||||
font-size: 20px;
|
||||
|
||||
& .option-action-container {
|
||||
font-size: 20px;
|
||||
font-size: 16px;
|
||||
|
||||
& .mobilefont {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
& .mobilefont + .mobilefont {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
& .van-icon + .van-icon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
& .van-icon + .mobilefont {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="question-action-container flex">
|
||||
<van-icon name="clear" @click="deleteQuestion"></van-icon>
|
||||
<van-icon name="setting" @click="openQuestionSettingModel"></van-icon>
|
||||
<van-icon name="more" @click="openQuestionActionModel"></van-icon>
|
||||
<van-icon class-prefix="mobilefont" name="setting" @click="openQuestionSettingModel"></van-icon>
|
||||
<van-icon class-prefix="mobilefont" name="gengduo " @click="openQuestionActionModel"></van-icon>
|
||||
<van-icon class-prefix="mobilefont" name="delete" @click="deleteQuestion"></van-icon>
|
||||
</div>
|
||||
|
||||
<!-- 操作项弹窗-->
|
||||
@@ -179,7 +179,8 @@ const saveSettings = () => {
|
||||
emit('setting', activeQuestion.value);
|
||||
};
|
||||
const saveLogics = () => {
|
||||
emit('logics', activeQuestion.value); // 将更新后的 questionsInfo 传递给父组件
|
||||
// 将更新后的 questionsInfo 传递给父组件
|
||||
emit('logics', activeQuestion.value);
|
||||
};
|
||||
|
||||
// 当前题目
|
||||
@@ -259,8 +260,8 @@ const getSkipTypeText = (skipType) => {
|
||||
const ls = [];
|
||||
logics.map((item) => {
|
||||
if (
|
||||
item.skip_type === skipType
|
||||
&& item.question_index === activeQuestion.value.question_index
|
||||
item.skip_type === skipType &&
|
||||
item.question_index === activeQuestion.value.question_index
|
||||
) {
|
||||
ls.push(item);
|
||||
}
|
||||
@@ -315,8 +316,16 @@ const updateConfig = (value) => {
|
||||
.question-action-container {
|
||||
font-size: 20px;
|
||||
|
||||
& .mobilefont {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
& .van-icon + .van-icon {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
& .mobilefont + .mobilefont {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -13,6 +13,6 @@
|
||||
import { completionOptions } from '@/utils/questionSteeingList.js';
|
||||
import YlSelect from '@/components/YLSelect.vue';
|
||||
import YlInput from '@/components/YLInput.vue';
|
||||
const logic = defineModel('logic', {});
|
||||
const logic = defineModel('logic');
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -16,8 +16,18 @@
|
||||
import { completionOptions } from '@/utils/questionSteeingList.js';
|
||||
import YlSelect from '@/components/YLSelect.vue';
|
||||
import YlInput from '@/components/YLInput.vue';
|
||||
const logic = defineModel('logic', {});
|
||||
const beforeQuesOptions = defineModel('beforeQuesOptions', {});
|
||||
const logic = defineModel('logic', {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
const beforeQuesOptions = defineModel('beforeQuesOptions', {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const getOptions = (type) => {
|
||||
let options = [];
|
||||
|
||||
@@ -20,8 +20,18 @@
|
||||
<script setup>
|
||||
import { groupOptions, symbolOptions } from '@/utils/questionSteeingList.js';
|
||||
import YlSelect from '@/components/YLSelect.vue';
|
||||
const logic = defineModel('logic');
|
||||
const beforeQuesOptions = defineModel('beforeQuesOptions');
|
||||
const logic = defineModel('logic', {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
const beforeQuesOptions = defineModel('beforeQuesOptions', {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
|
||||
const getOptions = (type) => {
|
||||
console.log(beforeQuesOptions);
|
||||
|
||||
@@ -55,8 +55,18 @@ const changeQuestionIndex = () => {
|
||||
|
||||
return options;
|
||||
};
|
||||
const beforeQuesOptions = defineModel('beforeQuesOptions');
|
||||
const logic = defineModel('logic', {});
|
||||
const logic = defineModel('logic', {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
const beforeQuesOptions = defineModel('beforeQuesOptions', {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
watch(
|
||||
() => logic.value,
|
||||
(newVal, oldVal) => {
|
||||
|
||||
@@ -12,6 +12,11 @@
|
||||
import { symbolOptions } from '@/utils/questionSteeingList.js';
|
||||
import YlSelect from '@/components/YLSelect.vue';
|
||||
import YlInput from '@/components/YLInput.vue';
|
||||
const logic = defineModel('logic');
|
||||
const logic = defineModel('logic', {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -356,7 +356,12 @@ const props = defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const questionsInfo = defineModel('questionsInfo');
|
||||
const questionsInfo = defineModel('questionsInfo', {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
});
|
||||
const logics = ref(questionsInfo.value.logics);
|
||||
const questions = questionsInfo.value.questions;
|
||||
|
||||
|
||||
@@ -108,9 +108,9 @@ function isSurplus() {
|
||||
return false;
|
||||
} else {
|
||||
return (
|
||||
parseFloat(((localConfig.value.max - localConfig.value.min) * 1000).toFixed(4, 10))
|
||||
% parseFloat((localConfig.value.score_interval * 1000).toFixed(4, 10))
|
||||
=== 0
|
||||
parseFloat(((localConfig.value.max - localConfig.value.min) * 1000).toFixed(4, 10)) %
|
||||
parseFloat((localConfig.value.score_interval * 1000).toFixed(4, 10)) ===
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,6 @@
|
||||
<template #title>
|
||||
<span class="title"> {{ getQuestionType(element.question_type) }}</span>
|
||||
</template>
|
||||
</van-cell>
|
||||
<slot></slot>
|
||||
<!-- 题目操作-->
|
||||
<van-cell v-if="chooseQuestionId === element.id" class="choose-question-active-container">
|
||||
<template #icon>
|
||||
<slot name="action" :element="element" :index="index"></slot>
|
||||
</template>
|
||||
<template #title>{{}}</template>
|
||||
<template #right-icon>
|
||||
<question-action
|
||||
v-model:data="element"
|
||||
@@ -32,6 +24,14 @@
|
||||
></question-action>
|
||||
</template>
|
||||
</van-cell>
|
||||
<slot></slot>
|
||||
<!-- 题目操作-->
|
||||
<van-cell v-if="chooseQuestionId === element.id" class="choose-question-active-container">
|
||||
<template #icon>
|
||||
<slot name="action" :element="element" :index="index"></slot>
|
||||
</template>
|
||||
<template #title>{{}}</template>
|
||||
</van-cell>
|
||||
</div>
|
||||
<div v-else>
|
||||
<slot></slot>
|
||||
|
||||
@@ -2,17 +2,28 @@
|
||||
<van-field
|
||||
v-model="element.stem" :label="element.stem" :required="element.config.is_required === 1" label-align="top"
|
||||
class="base-select"
|
||||
v-model="element.stem"
|
||||
:label="element.stem"
|
||||
:required="element.config.is_required === 1"
|
||||
label-align="top"
|
||||
class="contenteditable-question-title base-select"
|
||||
>
|
||||
<template #left-icon>
|
||||
{{ index + 1 }}
|
||||
</template>
|
||||
<template #label>
|
||||
<contenteditable v-model="element.stem" :active="active" @blur="emitValue"></contenteditable>
|
||||
<contenteditable
|
||||
v-model="element.stem"
|
||||
className="contenteditable-label"
|
||||
:active="active"
|
||||
@blur="emitValue"
|
||||
></contenteditable>
|
||||
</template>
|
||||
<template #input>
|
||||
<template v-for="(item, optionIndex) in element.options" :key="item.id">
|
||||
<van-radio-group v-if="element.question_type === 1" v-model="choiceValue">
|
||||
<option-action :data="isPreview ? item.options : item" :active="active" :question="element">
|
||||
<option-action :data="isPreview ? item.options : item" :active="active" :question="element"handle=".moverQues"
|
||||
>
|
||||
<template #item="{ element: it, index: itIndex }">
|
||||
<van-radio
|
||||
:key="itIndex" :name="it.option_index" :label="it.label" :disabled="it.disabled"
|
||||
@@ -21,7 +32,17 @@
|
||||
<!-- 自定义文本 -->
|
||||
<template #default>
|
||||
<div class="flex align-center van-cell">
|
||||
<contenteditable v-model="it.option" :active="active"></contenteditable>
|
||||
<contenteditable
|
||||
v-model="it.option"
|
||||
className="contenteditable-input"
|
||||
:active="active"
|
||||
>
|
||||
<template #right-icon>
|
||||
<div v-if="active" class="moverQues">
|
||||
<van-icon class-prefix="mobilefont" name="option "></van-icon>
|
||||
</div>
|
||||
</template>
|
||||
</contenteditable>
|
||||
<div v-if="it.is_other">
|
||||
<input class="other-input" type="text" />
|
||||
</div>
|
||||
@@ -31,8 +52,9 @@
|
||||
</template>
|
||||
</option-action>
|
||||
</van-radio-group>
|
||||
|
||||
<van-checkbox-group v-if="element.question_type === 2" v-model="value" shape="square">
|
||||
<option-action v-model:data="element.options[optionIndex]" :active="active" :question="element">
|
||||
<option-action v-model:data="element.options[optionIndex]"handle=".moverQues" :active="active" :question="element">
|
||||
<template #item="{ element: it, index: itIndex }">
|
||||
<van-checkbox
|
||||
:key="itIndex" :name="it.option_index" :label="it.label" :disabled="it.disabled"
|
||||
@@ -40,7 +62,17 @@
|
||||
>
|
||||
<template #default>
|
||||
<div class="flex align-center van-cell">
|
||||
<contenteditable v-model="it.option" :active="active"></contenteditable>
|
||||
<contenteditable
|
||||
v-model="it.option"
|
||||
className="contenteditable-input"
|
||||
:active="active"
|
||||
>
|
||||
<template #right-icon>
|
||||
<div v-if="active" class="moverQues">
|
||||
<van-icon class-prefix="mobilefont" name="option "></van-icon>
|
||||
</div>
|
||||
</template>
|
||||
</contenteditable>
|
||||
<div v-if="it.is_other">
|
||||
<input class="other-input" type="text" />
|
||||
</div>
|
||||
|
||||
@@ -63,11 +63,16 @@ const emitValue = () => {
|
||||
width: 100%;
|
||||
min-height: 40px;
|
||||
margin-bottom: 10px;
|
||||
padding: 3px 5px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 12px;
|
||||
border: 1px solid #f4f4f4;
|
||||
border-radius: 5px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.other_input::placeholder {
|
||||
color: #d7d7d7;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.text_value {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<p>{{ element.config.prompt_center }}</p>
|
||||
<p>{{ element.config.prompt_right }}</p>
|
||||
</div>
|
||||
<RateCharacter v-model="answerValue" :config="element.config"></RateCharacter>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -100,6 +101,6 @@ const chooseOption = (item) => {
|
||||
.tips {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #bfbfbf;
|
||||
color: #b8b8b8;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
:label="element.stem"
|
||||
:required="element.config.is_required === 1"
|
||||
label-align="top"
|
||||
class="contenteditable-question-title"
|
||||
>
|
||||
<template #left-icon>
|
||||
{{ index + 1 }}
|
||||
|
||||
@@ -102,7 +102,7 @@ ul {
|
||||
.rate_item {
|
||||
margin: 0 3px;
|
||||
margin-top: 5px;
|
||||
padding: 0 7px;
|
||||
padding: 0 6px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
color: #666;
|
||||
|
||||
@@ -12,8 +12,7 @@ const { element, active } = toRefs(props);
|
||||
const signatureCanvas = useTemplateRef('signatureCanvas');
|
||||
|
||||
const canvasWidth = ref(100);
|
||||
const canvasHeight = computed(() => canvasWidth.value / 2.5);
|
||||
const showSignText = ref(true);
|
||||
const canvasHeight = computed(() => canvasWidth.value / 1.5);
|
||||
const isEraser = ref(false);
|
||||
|
||||
let ctx: CanvasRenderingContext2D;
|
||||
@@ -60,7 +59,7 @@ const togglePen = () => {
|
||||
onMounted(() => {
|
||||
if (!signatureCanvas.value) return;
|
||||
// 将 canvas 宽度和窗口的宽度保持一致
|
||||
canvasWidth.value = window.innerWidth - 50;
|
||||
canvasWidth.value = window.innerWidth - 60;
|
||||
|
||||
// 获取 canvas 上下文
|
||||
ctx = signatureCanvas.value.getContext('2d')!;
|
||||
@@ -170,24 +169,19 @@ const emitValue = () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<van-cell>
|
||||
<!-- <van-cell>-->
|
||||
<van-field
|
||||
:label="element.stem"
|
||||
:required="element.config.is_required === 1"
|
||||
label-align="top"
|
||||
:border="false"
|
||||
readonly
|
||||
class="base-select"
|
||||
>
|
||||
<template #left-icon>
|
||||
{{ index + 1 }}
|
||||
</template>
|
||||
<template #label>
|
||||
<contenteditable
|
||||
v-model="element.stem"
|
||||
:active="active"
|
||||
@blur="emitValue"
|
||||
></contenteditable>
|
||||
<contenteditable v-model="element.stem" :active="active" @blur="emitValue"></contenteditable>
|
||||
</template>
|
||||
<template #input>
|
||||
<div class="sign-question">
|
||||
@@ -195,19 +189,28 @@ const emitValue = () => {
|
||||
ref="signatureCanvas"
|
||||
:width="canvasWidth"
|
||||
:height="canvasHeight"
|
||||
style="border: 1px solid #ccc; border-radius: 4px"
|
||||
style="border: 1px dashed #ccc; border-radius: 4px"
|
||||
>
|
||||
</canvas>
|
||||
<div class="sign-text" :class="{ show: showSignText }">
|
||||
<span @click="clearCanvas">清空</span>
|
||||
<span @click="undo">撤销</span>
|
||||
<span @click="togglePen">{{ isEraser ? '画笔' : '橡皮擦' }}</span>
|
||||
<span @click="saveCanvas">完成并上传</span>
|
||||
<div v-if="active" class="sign-text" :class="{ show: active }">
|
||||
<span
|
||||
class="icon mobilefont mobilefont-qingkong"
|
||||
title="清空"
|
||||
@click="clearCanvas"
|
||||
></span>
|
||||
<span class="icon mobilefont mobilefont-chexiao" @click="undo"></span>
|
||||
<span
|
||||
class="icon mobilefont"
|
||||
:class="isEraser ? 'mobilefont-huabi' : 'mobilefont-rubber'"
|
||||
@click="togglePen"
|
||||
></span>
|
||||
<span class="icon mobilefont mobilefont-shangchuan" @click="saveCanvas"></span>
|
||||
</div>
|
||||
<div v-else class="sign-tips">请在空白区域书写您的签名</div>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
</van-cell>
|
||||
<!-- </van-cell>-->
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -255,5 +258,14 @@ const emitValue = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sign-tips {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: max-content;
|
||||
color: #cdcdcd;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
class="iconfont active-icon"
|
||||
:style="{ marginRight: isLastPage ? '0' : '16px' }"
|
||||
@click="activePage"
|
||||
></i>
|
||||
></i
|
||||
>
|
||||
<template v-if="!isLastPage">
|
||||
<i class="iconfont moverQues" style="margin-right: 16px"></i>
|
||||
<i class="iconfont" @click="deleteHandle"></i>
|
||||
|
||||
@@ -7,7 +7,9 @@ import { onMounted, ref } from 'vue';
|
||||
// import { getUserInfo } from '@/api/common/index.js';
|
||||
// import { showFailToast } from 'vant';
|
||||
const contentShow = ref(false);
|
||||
onMounted(async() => {
|
||||
const show = ref(false);
|
||||
|
||||
onMounted(async () => {
|
||||
contentShow.value = true;
|
||||
// if (utils.getSessionStorage('xToken')) {
|
||||
// const appToken = utils.getParameter('digitalYiliToken');
|
||||
@@ -27,32 +29,41 @@ onMounted(async() => {
|
||||
// contentShow.value = true;
|
||||
// }
|
||||
});
|
||||
function create() {
|
||||
show.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="contentShow" class="container">
|
||||
<create-survey />
|
||||
|
||||
<!-- 最新问卷 -->
|
||||
<last-survey />
|
||||
|
||||
<!-- 模板市场 -->
|
||||
<Market />
|
||||
<div class="new_survey">
|
||||
<van-button type="primary" block color="#70B937" @click="create">
|
||||
<span class="fw-bold">+</span> 新建问卷
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
<van-popup v-model:show="show" round closeable position="bottom">
|
||||
<CreateSurvey></CreateSurvey>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.container {
|
||||
padding: 0 10px 60px;
|
||||
padding: 30px 10px 80px;
|
||||
background: linear-gradient(0deg, #f5f5f5 0%, #f5f5f5 84%, #a5d380 100%);
|
||||
|
||||
&> :first-child {
|
||||
& > :first-child {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
border-radius: 6px;
|
||||
background-color: white;
|
||||
|
||||
&>div {
|
||||
& > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 50px;
|
||||
@@ -64,5 +75,16 @@ onMounted(async() => {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.new_survey {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
box-sizing: border-box;
|
||||
width: 100vw;
|
||||
padding: 12px 11px 28px;
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -70,7 +70,7 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<van-cell class="create_survey">
|
||||
<div style="color: #000; text-align: left">新建问卷</div>
|
||||
<div class="create_survey_title" style="color: #000; text-align: left">请选择新建类型</div>
|
||||
<van-row>
|
||||
<van-col
|
||||
v-for="survey in surveys"
|
||||
@@ -90,6 +90,14 @@ onMounted(() => {
|
||||
.create_survey {
|
||||
padding: 15px;
|
||||
color: #000;
|
||||
|
||||
.create_survey_title {
|
||||
margin-bottom: 15px;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
font-family: PingFangSC, 'PingFang SC';
|
||||
}
|
||||
}
|
||||
|
||||
.survey {
|
||||
@@ -98,13 +106,12 @@ onMounted(() => {
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
img {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: black;
|
||||
font-size: 10px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 18px;
|
||||
color: #000;
|
||||
font-weight: 400;
|
||||
font-size: 0.34rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<p class="fw-bold">最新问卷</p>
|
||||
<p class="survey_header_tag">NEW</p>
|
||||
</div>
|
||||
<p>全部问卷 ></p>
|
||||
<p @click="$router.push('/survey')">全部问卷 ></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="survey_con">
|
||||
@@ -20,11 +20,11 @@
|
||||
<div class="survey_con_label flex">
|
||||
<div class="flex">
|
||||
<img src="" alt="" />
|
||||
{{ survey.scene_name }}
|
||||
{{ survey.scene_name }} |
|
||||
</div>
|
||||
<div class="flex">
|
||||
<img src="" alt="" />
|
||||
{{ survey.source === 1 ? '移动端' : 'PC端' }}
|
||||
{{ survey.source === 1 ? '移动端' : 'PC端' }} |
|
||||
</div>
|
||||
<div class="flex">
|
||||
<img src="" alt="" />
|
||||
@@ -46,7 +46,7 @@ import { consoleSurveys } from '@/api/home/index.js';
|
||||
const survey = ref({
|
||||
project_name: ''
|
||||
});
|
||||
const fetchSurveys = async() => {
|
||||
const fetchSurveys = async () => {
|
||||
const params = {
|
||||
page: 1,
|
||||
per_page: 10,
|
||||
@@ -117,18 +117,22 @@ onMounted(() => {
|
||||
.survey_con_label {
|
||||
margin-left: 10px;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
font-size: 10px !important;
|
||||
|
||||
div {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.survey_remark {
|
||||
box-sizing: border-box;
|
||||
margin: 10px 0 0 10px;
|
||||
padding-bottom: 10px;
|
||||
padding: 10px 5px;
|
||||
border-radius: 10px;
|
||||
color: #828282;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,8 +106,8 @@ onMounted(() => {
|
||||
}
|
||||
|
||||
.more {
|
||||
margin: 20px auto;
|
||||
color: #000;
|
||||
margin: 20px;
|
||||
color: #919191;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
<div class="content">
|
||||
<div class="title fw-bold fs-14">
|
||||
<div class="flex align-center">
|
||||
<component :is="contentSvg"></component>
|
||||
<img src="./svgs/contentSvg.svg" alt="Content Icon" style="width: 14px" />
|
||||
<p class="title_con">{{ item.title }}</p>
|
||||
</div>
|
||||
<component :is="MarketItemSvg"></component>
|
||||
<img src="./svgs/MarketItemSvg.svg" alt="Content Icon" style="width: 14px" />
|
||||
</div>
|
||||
<div class="desc flex space-between">
|
||||
<div>
|
||||
@@ -28,8 +28,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import contentSvg from './svgs/contentSvg.svg?componet';
|
||||
import MarketItemSvg from './svgs/MarketItemSvg.svg?componet';
|
||||
// import contentSvg from './svgs/contentSvg.svg';
|
||||
// import MarketItemSvg from './svgs/MarketItemSvg.svg';
|
||||
const { info } = defineProps({
|
||||
info: {
|
||||
type: Object,
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="2494"
|
||||
width="14"
|
||||
height="14"
|
||||
width="8"
|
||||
height="8"
|
||||
>
|
||||
<path
|
||||
d="M1024 354c0-1.8-0.2-3.6-0.5-5.4V158c0-51.8-42.2-94-94-94H798V30c0-16.5-13.5-30-30-30s-30 13.5-30 30v34H286V30c0-16.5-13.5-30-30-30s-30 13.5-30 30v34H94.5c-51.8 0-94 42.2-94 94v190.6c-0.3 1.8-0.5 3.6-0.5 5.4 0 1.8 0.2 3.6 0.5 5.4V930c0 51.8 42.2 94 94 94h835c51.8 0 94-42.2 94-94V359.4c0.3-1.8 0.5-3.6 0.5-5.4zM94.5 124H226v38c0 16.5 13.5 30 30 30s30-13.5 30-30v-38h452v38c0 16.5 13.5 30 30 30s30-13.5 30-30v-38h131.5c18.7 0 34 15.3 34 34v166h-903V158c0-18.7 15.3-34 34-34z m835 840h-835c-18.7 0-34-15.3-34-34V384h903v546c0 18.7-15.3 34-34 34z"
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -3,7 +3,9 @@
|
||||
<div v-for="item in 10" :key="item" class="template">
|
||||
<img src="https://picsum.photos/131/128" width="110" height="100" alt="" />
|
||||
<span>报名/签到模板</span>
|
||||
<span style="color: rgb(127, 127, 127)">报名签到 | 引用 {{ item }} 次 | 创建人: {{ '张三' }}</span>
|
||||
<span style="color: rgb(127, 127, 127)"
|
||||
>报名签到 | 引用 {{ item }} 次 | 创建人: {{ '张三' }}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -6,38 +6,60 @@
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
|
||||
<van-cell-group inset class="result-cell">
|
||||
<div>
|
||||
<div>
|
||||
<div class="question-title flex space-between">
|
||||
<div class="title-left">
|
||||
<!--问卷标题-->
|
||||
<contenteditable
|
||||
v-model="questionInfo.survey.title"
|
||||
className="content-title"
|
||||
:active="true"
|
||||
@blur="saveTitle"
|
||||
></contenteditable>
|
||||
</div>
|
||||
<div>
|
||||
<!-- 问卷标注-->
|
||||
<contenteditable
|
||||
v-model="questionInfo.survey.introduction"
|
||||
className="introduction"
|
||||
:active="true"
|
||||
@blur="saveTitle"
|
||||
></contenteditable>
|
||||
</div>
|
||||
<!-- <van-cell-group inset class="result-cell">-->
|
||||
<!-- <div>-->
|
||||
<!-- <div>-->
|
||||
<!-- -->
|
||||
<!-- </div>-->
|
||||
<!-- <div>-->
|
||||
<!-- -->
|
||||
<!-- </div>-->
|
||||
|
||||
<van-button v-if="questionInfo.questions.length === 0" size="small" @click="show = true">
|
||||
添加题目
|
||||
</van-button>
|
||||
<!-- </div>-->
|
||||
<!-- </van-cell-group>-->
|
||||
<div class="title-right">图标</div>
|
||||
</div>
|
||||
</van-cell-group>
|
||||
|
||||
<div class="ques">
|
||||
<!-- 题目-->
|
||||
<Design :active-id="activeId" class="design" @get-active-question="getActiveQuestion">
|
||||
<Design :activeId="activeId" class="design" @get-active-question="getActiveQuestion">
|
||||
<template #button="{ item }">
|
||||
<div class="design-button">
|
||||
<van-button v-if="activeId === item.id" size="small" @click="show = true">
|
||||
添加题目
|
||||
+ 添加题目
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<div class="empty">
|
||||
<div>
|
||||
<img :src="emptyImage" alt="" class="empty-image" />
|
||||
</div>
|
||||
<van-button
|
||||
v-if="questionInfo.questions.length === 0"
|
||||
class="theme-background"
|
||||
size="small"
|
||||
@click="show = true"
|
||||
>
|
||||
+ 添加题目
|
||||
</van-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -75,26 +97,29 @@
|
||||
|
||||
<!-- 底部功能性按钮 -->
|
||||
<div class="survey-action">
|
||||
<div class="survey-action_setting" @click="openSettingAction">
|
||||
<van-icon name="setting" size="18" class="grid-icon" />
|
||||
<span>投放设置</span>
|
||||
</div>
|
||||
<!-- <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" @click="previewQuestion">预览</van-button>
|
||||
<van-button size="small" @click="saveAs">保存</van-button>
|
||||
<van-button size="small" @click="publishQuestion">投放</van-button>
|
||||
<van-button size="small" plain type="primary" @click="saveAs">保存</van-button>
|
||||
<van-button size="small" plain type="primary" @click="previewQuestion">预览</van-button>
|
||||
<van-button size="small" plain type="success" @click="openSettingAction">
|
||||
投放设置
|
||||
</van-button>
|
||||
<van-button size="small" icon="guide-o" @click="publishQuestion">立即投放</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 投放设置-->
|
||||
|
||||
<van-action-sheet v-model:show="showSetting" title="">
|
||||
<van-action-sheet v-model:show="showSetting" title="" style="background-color: #f2f2f2">
|
||||
<template #description>
|
||||
<div class="flex flex-start">设置</div>
|
||||
<div class="setting_title flex flex-start">投放设置</div>
|
||||
</template>
|
||||
<van-cell-group :border="false" class="ml10">
|
||||
<van-cell title="每页一题" :border="false" label-align="left">
|
||||
<van-cell-group :border="false" class="setting_group">
|
||||
<van-cell title="每页一题" class="setting_block" :border="false" label-align="left">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="questionInfo.survey.is_one_page_one_question"
|
||||
@@ -107,7 +132,7 @@
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-divider></van-divider>
|
||||
<van-cell title="投放数量" :border="false" label-align="left">
|
||||
<van-cell title="投放数量" class="setting_block" :border="false" label-align="left">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="questionInfo.survey.is_publish_number"
|
||||
@@ -121,7 +146,7 @@
|
||||
</van-cell>
|
||||
<van-cell-group
|
||||
v-if="questionInfo.survey.is_publish_number === 1"
|
||||
class="child-group"
|
||||
class="child-group setting_block"
|
||||
:border="false"
|
||||
>
|
||||
<van-field
|
||||
@@ -135,7 +160,7 @@
|
||||
</van-field>
|
||||
</van-cell-group>
|
||||
<van-divider></van-divider>
|
||||
<van-cell title="有效期" :border="false" label-align="left">
|
||||
<van-cell title="有效期" class="border-top" :border="false" label-align="left">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="questionInfo.survey.is_time"
|
||||
@@ -184,6 +209,7 @@
|
||||
|
||||
<van-cell
|
||||
title="企微身份获取(开启后仅支持私有化企业环境作答)"
|
||||
class="border-bottom"
|
||||
:border="false"
|
||||
label-align="left"
|
||||
>
|
||||
@@ -199,7 +225,7 @@
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-divider></van-divider>
|
||||
<van-cell title="IP答题次数限制" :border="false" label-align="left">
|
||||
<van-cell title="IP答题次数限制" class="border-top" :border="false" label-align="left">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="questionInfo.survey.is_ip_number"
|
||||
@@ -222,7 +248,7 @@
|
||||
<template #right-icon> 次 </template>
|
||||
</van-field>
|
||||
</van-cell-group>
|
||||
<van-cell title="设备答题次数限制" :border="false" label-align="left">
|
||||
<van-cell title="设备答题次数限制" class="border-bottom" :border="false" label-align="left">
|
||||
<template #right-icon>
|
||||
<van-switch
|
||||
v-model="questionInfo.survey.is_browser_number"
|
||||
@@ -284,6 +310,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import emptyImage from '@/assets/img/empty.png';
|
||||
import { ref, computed, onMounted, watch } from 'vue';
|
||||
import * as Base64 from 'js-base64';
|
||||
import {
|
||||
@@ -520,6 +547,8 @@ onMounted(async() => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/assets/css/theme';
|
||||
|
||||
::v-deep .van-nav-bar {
|
||||
background-color: #70b937;
|
||||
color: #fff;
|
||||
@@ -533,6 +562,28 @@ onMounted(async() => {
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
& .question-title {
|
||||
padding: 0 20px;
|
||||
background-color: transparent !important;
|
||||
|
||||
& .title-left {
|
||||
color: #fff;
|
||||
|
||||
::v-deep .content-title {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
& .title-right {
|
||||
margin-right: 50px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .van-hairline--bottom::after {
|
||||
display: none;
|
||||
}
|
||||
@@ -544,7 +595,10 @@ onMounted(async() => {
|
||||
button {
|
||||
padding: 3px 20px;
|
||||
border: none;
|
||||
background-color: #f2f2f2;
|
||||
border-radius: 10px;
|
||||
background-color: #70b937;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
* {
|
||||
@@ -552,6 +606,37 @@ onMounted(async() => {
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .van-popup--bottom.van-popup--round {
|
||||
background-color: #f2f2f2 !important;
|
||||
}
|
||||
|
||||
.setting_title {
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
font-size: 19px;
|
||||
font-family: PingFangSC, 'PingFang SC';
|
||||
line-height: 26px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.setting_group {
|
||||
padding: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.setting_block {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.border-top {
|
||||
border-radius: 12px 12px 0 0;
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
border-radius: 0 0 12px 12px;
|
||||
}
|
||||
|
||||
.result-cell {
|
||||
margin: 10px 5px;
|
||||
padding: 5px;
|
||||
@@ -561,14 +646,6 @@ onMounted(async() => {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
|
||||
& > button {
|
||||
margin: 20px;
|
||||
|
||||
//border-radius: 10px;
|
||||
background-color: #70b936;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,6 +665,45 @@ onMounted(async() => {
|
||||
.ques {
|
||||
min-height: 70vh;
|
||||
|
||||
.design-button {
|
||||
text-align: center;
|
||||
|
||||
& > button {
|
||||
padding: 0.5rem;
|
||||
border-radius: 8px;
|
||||
background: rgba(240, 248, 235, 1);
|
||||
color: $theme-color;
|
||||
|
||||
//opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
overflow: hidden;
|
||||
height: 250px;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
|
||||
& .empty-image {
|
||||
width: 186px;
|
||||
height: 114px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
& > button {
|
||||
margin-top: 40px;
|
||||
padding: 0.5rem;
|
||||
border-radius: 5px;
|
||||
background: rgba(240, 248, 235, 1);
|
||||
color: $theme-color;
|
||||
|
||||
//opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
.ques_title {
|
||||
margin: 20px 0 10px 20px;
|
||||
font-weight: bold;
|
||||
@@ -596,19 +712,6 @@ onMounted(async() => {
|
||||
|
||||
& .design {
|
||||
padding-bottom: 60px;
|
||||
|
||||
& .design-button {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
::v-deep .van-button {
|
||||
background-color: #70b936;
|
||||
|
||||
//width: 140px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ques_list {
|
||||
@@ -630,6 +733,24 @@ onMounted(async() => {
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .van-button--plain.van-button--primary {
|
||||
padding: 13px 15px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
color: #4b4b59;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
::v-deep .van-button--plain.van-button--success {
|
||||
padding: 13px 15px;
|
||||
border: 1px solid #70b937;
|
||||
border-radius: 10px;
|
||||
background-color: #fff;
|
||||
color: #70b937;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.survey-action {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
@@ -639,6 +760,7 @@ onMounted(async() => {
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
border-radius: 10px 10px 0 0;
|
||||
|
||||
//margin: 0 10px;
|
||||
background-color: white;
|
||||
@@ -657,7 +779,7 @@ onMounted(async() => {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-right: 10px;
|
||||
margin-left: 8px;
|
||||
|
||||
& .van-button + .van-button {
|
||||
margin-left: 10px;
|
||||
|
||||
@@ -227,8 +227,8 @@ export default defineComponent({
|
||||
.map((option) => {
|
||||
return this.wares.find(
|
||||
(ware) =>
|
||||
ware.question_index === option.question_index
|
||||
&& ware.option_index === option.option_index
|
||||
ware.question_index === option.question_index &&
|
||||
ware.option_index === option.option_index
|
||||
);
|
||||
});
|
||||
wares = wares.filter((x) => x);
|
||||
|
||||
@@ -144,7 +144,7 @@ export default {
|
||||
methods: {
|
||||
debounce(func, wait) {
|
||||
let timeout;
|
||||
return function(...args) {
|
||||
return function (...args) {
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => func.apply(this, args), wait);
|
||||
};
|
||||
|
||||
@@ -102,11 +102,11 @@ export default defineComponent({
|
||||
const isEndUrl = computed(() => {
|
||||
const code = props.action ? props.action.code : props.code;
|
||||
return (
|
||||
(code === 20004
|
||||
&& props.survey.screening_end_url_select
|
||||
&& props.survey.screening_end_url)
|
||||
|| (code === 20011 && props.survey.success_end_url_select && props.survey.success_end_url)
|
||||
|| (code === 20016 && props.survey.quota_end_url_select && props.survey.quota_end_url)
|
||||
(code === 20004 &&
|
||||
props.survey.screening_end_url_select &&
|
||||
props.survey.screening_end_url) ||
|
||||
(code === 20011 && props.survey.success_end_url_select && props.survey.success_end_url) ||
|
||||
(code === 20016 && props.survey.quota_end_url_select && props.survey.quota_end_url)
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -264,16 +264,16 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
if (
|
||||
!compareArrayByField(options.value, newOptions, 'option_key')
|
||||
|| !compareArrayByField(options.value, newOptions, 'option')
|
||||
!compareArrayByField(options.value, newOptions, 'option_key') ||
|
||||
!compareArrayByField(options.value, newOptions, 'option')
|
||||
) {
|
||||
options.value = newOptions;
|
||||
}
|
||||
|
||||
// 清空值和答案
|
||||
if (
|
||||
value.value
|
||||
&& options.value.findIndex((option) => option.option_key === value.value) === -1
|
||||
value.value &&
|
||||
options.value.findIndex((option) => option.option_key === value.value) === -1
|
||||
) {
|
||||
// 清空值
|
||||
value.value = '';
|
||||
@@ -291,8 +291,8 @@ export default defineComponent({
|
||||
() => options.value,
|
||||
(val, oldVal) => {
|
||||
if (
|
||||
compareArrayByField(val, oldVal || [], 'option_key')
|
||||
&& compareArrayByField(val, oldVal || [], 'option')
|
||||
compareArrayByField(val, oldVal || [], 'option_key') &&
|
||||
compareArrayByField(val, oldVal || [], 'option')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -132,8 +132,8 @@ export default defineComponent({
|
||||
const value = matchValue.replace('[%cite(', '').replace(')%]', '');
|
||||
let replacement = ''; // 替换文本
|
||||
// 查找引用问题
|
||||
const question
|
||||
= props.questions.find((question) => {
|
||||
const question =
|
||||
props.questions.find((question) => {
|
||||
// 矩阵题
|
||||
if (question.question_type >= 8 && question.question_type <= 11) {
|
||||
return question.title === value.split('_R')[0].split('_C')[0];
|
||||
@@ -143,8 +143,8 @@ export default defineComponent({
|
||||
return question.title === value.split('_A')[0];
|
||||
}
|
||||
return question.title === value;
|
||||
})
|
||||
|| props.questions.find((question) => {
|
||||
}) ||
|
||||
props.questions.find((question) => {
|
||||
// 矩阵题
|
||||
if (question.question_type >= 8 && question.question_type <= 11) {
|
||||
return question.title === (value + cycleIndexStr).split('_R')[0].split('_C')[0];
|
||||
@@ -171,8 +171,8 @@ export default defineComponent({
|
||||
replacement = answer[option.option_key];
|
||||
}
|
||||
} else if (
|
||||
question.question_type === 2
|
||||
&& Object.keys(answer).length >= question.config.min_select
|
||||
question.question_type === 2 &&
|
||||
Object.keys(answer).length >= question.config.min_select
|
||||
) {
|
||||
// 查找引用选项(多选)
|
||||
options.forEach((option) => {
|
||||
|
||||
@@ -350,7 +350,7 @@ export const language = {
|
||||
zh: '请点击查看图片'
|
||||
},
|
||||
NoteCantViewAfterTimeLimit: {
|
||||
en: 'Note: Can\'t view after time limit',
|
||||
en: "Note: Can't view after time limit",
|
||||
zh: '注意:超过显示时间限制后将无法再次查看'
|
||||
},
|
||||
DisplayTimeLimitExceeded: {
|
||||
|
||||
@@ -250,10 +250,10 @@ function quesHandle(answer, logChild) {
|
||||
const matrixRateHandle = () => {
|
||||
// 如果配置的逻辑中参数为空,则代表没有配置逻辑匹配值,此时不做校验
|
||||
if (
|
||||
logChild.cell_index === 0
|
||||
|| logChild.row_index === 0
|
||||
|| !logChild.operator
|
||||
|| !logChild.value
|
||||
logChild.cell_index === 0 ||
|
||||
logChild.row_index === 0 ||
|
||||
!logChild.operator ||
|
||||
!logChild.value
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -265,10 +265,10 @@ function quesHandle(answer, logChild) {
|
||||
const matrixInputHandle = () => {
|
||||
// 如果配置的逻辑中参数为空,则代表没有配置逻辑匹配值,此时不做校验
|
||||
if (
|
||||
logChild.cell_index === 0
|
||||
|| logChild.row_index === 0
|
||||
|| !logChild.operator
|
||||
|| !logChild.value
|
||||
logChild.cell_index === 0 ||
|
||||
logChild.row_index === 0 ||
|
||||
!logChild.operator ||
|
||||
!logChild.value
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
@@ -519,10 +519,10 @@ function getlogicStatus(questionData) {
|
||||
// } else {
|
||||
// statusStr = statusStr + conditionStatus;
|
||||
// }
|
||||
statusStr
|
||||
= statusStr
|
||||
+ (logChild.logic === 'and' ? '&&' : logChild.logic === 'or' ? '||' : '')
|
||||
+ conditionStatus;
|
||||
statusStr =
|
||||
statusStr +
|
||||
(logChild.logic === 'and' ? '&&' : logChild.logic === 'or' ? '||' : '') +
|
||||
conditionStatus;
|
||||
});
|
||||
// eslint-disable-next-line no-eval
|
||||
logs.logicStatus = eval(statusStr);
|
||||
|
||||
@@ -105,8 +105,8 @@ export default function answerMock(questionsData, page) {
|
||||
} else if (logic.skip_type === 4) {
|
||||
// 只计算跳转后所在页面的隐藏逻辑,否则会出现只返回最后一道隐藏选项题目的情况,导致失效
|
||||
const toPage = page + 1;
|
||||
const hasHiddenLogicQuizPage
|
||||
= data.pages.findIndex((page) => page.includes(logic.question_index)) + 1;
|
||||
const hasHiddenLogicQuizPage =
|
||||
data.pages.findIndex((page) => page.includes(logic.question_index)) + 1;
|
||||
if (hasHiddenLogicQuizPage === toPage) {
|
||||
// 选项隐藏逻辑
|
||||
updateOptionHidden(data.hide_options, logic);
|
||||
|
||||
@@ -154,9 +154,9 @@ export default defineComponent({
|
||||
question.error = translatedText.value.ThisIsARequiredQuestion;
|
||||
}
|
||||
} else if (
|
||||
answer
|
||||
&& questionType === 1
|
||||
&& Object.keys(answer).findIndex((value) => !answer[value]) !== -1
|
||||
answer &&
|
||||
questionType === 1 &&
|
||||
Object.keys(answer).findIndex((value) => !answer[value]) !== -1
|
||||
) {
|
||||
// 单选题
|
||||
isError = true;
|
||||
@@ -302,9 +302,9 @@ export default defineComponent({
|
||||
} else if (answer && questionType === 12) {
|
||||
question.error = '';
|
||||
} else if (
|
||||
answer
|
||||
&& questionType === 14
|
||||
&& Object.keys(answer).length < config.min_select
|
||||
answer &&
|
||||
questionType === 14 &&
|
||||
Object.keys(answer).length < config.min_select
|
||||
) {
|
||||
// 图片多选题
|
||||
isError = true;
|
||||
@@ -341,8 +341,8 @@ export default defineComponent({
|
||||
const newValue = value.replace(/\n|\r|\r\n/g, '');
|
||||
switch (config.text_type) {
|
||||
case 3: // 字母
|
||||
isError
|
||||
= config.include_mark === 1
|
||||
isError =
|
||||
config.include_mark === 1
|
||||
? !/^[a-zA-Z·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]]+$/.test(
|
||||
newValue
|
||||
) || !newValue.length
|
||||
@@ -350,8 +350,8 @@ export default defineComponent({
|
||||
question.error = isError ? translatedText.value.PleaseEnterEnglishLetters : '';
|
||||
break;
|
||||
case 4: // 中文
|
||||
isError
|
||||
= config.include_mark === 1
|
||||
isError =
|
||||
config.include_mark === 1
|
||||
? !/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|[·~!@#¥%……&*()——\-+={}|《》?:“”【】、;‘’,。、`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]])+$/.test(
|
||||
newValue
|
||||
) || !newValue.length
|
||||
@@ -361,8 +361,8 @@ export default defineComponent({
|
||||
question.error = isError ? translatedText.value.PleaseEnterChineseWords : '';
|
||||
break;
|
||||
case 5: // 邮箱
|
||||
isError
|
||||
= !/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
||||
isError =
|
||||
!/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
||||
value
|
||||
);
|
||||
question.error = isError ? translatedText.value.PleaseEnterACorrectEmail : '';
|
||||
@@ -372,8 +372,8 @@ export default defineComponent({
|
||||
question.error = isError ? translatedText.value.PleaseEnterACorrectPhone : '';
|
||||
break;
|
||||
case 7: // 身份证号
|
||||
isError
|
||||
= !/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
|
||||
isError =
|
||||
!/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0[1-9]|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/.test(
|
||||
value
|
||||
);
|
||||
question.error = isError ? translatedText.value.PleaseEnterACorrectID : '';
|
||||
@@ -436,9 +436,9 @@ export default defineComponent({
|
||||
break;
|
||||
}
|
||||
if (
|
||||
!question.error
|
||||
&& value.length < config.min
|
||||
&& ![1, 2].includes(config.text_type)
|
||||
!question.error &&
|
||||
value.length < config.min &&
|
||||
![1, 2].includes(config.text_type)
|
||||
) {
|
||||
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
|
||||
}
|
||||
@@ -814,8 +814,8 @@ export default defineComponent({
|
||||
const evt1 = {};
|
||||
|
||||
if ([1].includes(question.question_type)) {
|
||||
evt1.value
|
||||
= Object.keys(question.answer)
|
||||
evt1.value =
|
||||
Object.keys(question.answer)
|
||||
.map((key) => (question.answer[key] ? key : undefined))
|
||||
.filter((i) => !!i)?.[0] || undefined;
|
||||
evt1.options = question.list.flatMap((i) => i.options);
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<van-nav-bar :title="surveyTitle" left-arrow @click-left="$router.go(-1)" />
|
||||
<layout />
|
||||
<div class="content">
|
||||
<!-- <van-cell-group v-if="status === 1" inset style="padding-top: 15px"> -->
|
||||
<van-cell-group inset style="padding-top: 15px">
|
||||
<van-cell-group v-if="status === 1" inset style="padding-top: 15px">
|
||||
<div>
|
||||
<img
|
||||
width="100%"
|
||||
@@ -12,9 +11,9 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="qrcode">
|
||||
<img :src="publishInfo.img_url" alt="" width="100px" height="100px" />
|
||||
<img :src="publishInfo?.img_url" alt="" width="100px" height="100px" />
|
||||
<div class="tit">
|
||||
<div>液态奶产品研究标准化问卷</div>
|
||||
<div>{{ publishInfo?.download_url?.title || '' }}</div>
|
||||
<div>扫码填写问卷</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -26,52 +25,62 @@
|
||||
</span>
|
||||
</div>
|
||||
</van-cell-group>
|
||||
<!-- <div v-if="status === 0 || status === 2" class="pulish-container">
|
||||
<div v-if="status === 0 || status === 2" class="pulish-container">
|
||||
<img class="not-publish-icon" src="@/assets/img/publish/not_pulish.png" alt="" />
|
||||
<div class="text">点击"启用"按钮后,问卷才可以开始回收数据</div>
|
||||
<van-button type="primary" style="margin-top: 20px" class="publish-btn" color="#70b936"
|
||||
@click="openPublishModal">
|
||||
<van-button
|
||||
type="primary"
|
||||
style="margin-top: 20px"
|
||||
class="publish-btn"
|
||||
color="#70b936"
|
||||
@click="openPublishModal"
|
||||
>
|
||||
<template #icon>
|
||||
<i class="iconfont icon-fabu3" style="margin-right: 6px"></i>
|
||||
<i class="mobilefont icon-fabu3" style="margin-right: 6px"></i>
|
||||
</template>
|
||||
启用
|
||||
</van-button>
|
||||
</div> -->
|
||||
启用
|
||||
</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import layout from '@/layouts/index.vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { onMounted, reactive, ref, watch } from 'vue';
|
||||
import { showFailToast, showToast } from 'vant';
|
||||
import utils from '@/assets/js/common';
|
||||
import appBridge from '@/assets/js/appBridge';
|
||||
import { getQrcode } from '@/api/publish';
|
||||
// import { canPlanetPublish } from '@/layouts/utils.js';
|
||||
import configUrl from '../../../../config';
|
||||
import { getQrcode, getSurveyInfo, publishSurvey } from '@/api/survey';
|
||||
import { canPlanetPublish } from '@/layouts/utils.js';
|
||||
// import configUrl from '../../../../config';
|
||||
import copyIcon from '@/assets/img/publish/copy_icon.png';
|
||||
import shareIcon from '@/assets/img/publish/share_icon.png';
|
||||
import downloadIcon from '@/assets/img/publish/download_icon.png';
|
||||
|
||||
const route = useRoute();
|
||||
const surveyTitle = route.meta.title as string;
|
||||
appBridge.setTitle(surveyTitle);
|
||||
const sn = route.query.sn;
|
||||
const status = ref<number>(0); // `0`: 编辑中 `1`: 投放中 `2`: 已结束
|
||||
// const publishType = ref(0);
|
||||
const publishType = ref(0);
|
||||
const operateList = reactive([
|
||||
{
|
||||
title: '复制链接',
|
||||
type: 'copyLink',
|
||||
icon: 'https://files.axshare.com/gsc/DR6075/44/1a/03/441a03a8b1004755a7a392b311acf97f/images/%E6%8A%95%E6%94%BE/u21.png?pageId=2f9ba10c-92b8-4c9b-b40b-04e65a0b4333'
|
||||
icon: copyIcon
|
||||
// icon: 'https://files.axshare.com/gsc/DR6075/44/1a/03/441a03a8b1004755a7a392b311acf97f/images/%E6%8A%95%E6%94%BE/u21.png?pageId=2f9ba10c-92b8-4c9b-b40b-04e65a0b4333'
|
||||
},
|
||||
{
|
||||
title: '转发到微信',
|
||||
type: 'shareLink',
|
||||
icon: 'https://files.axshare.com/gsc/DR6075/44/1a/03/441a03a8b1004755a7a392b311acf97f/images/%E6%8A%95%E6%94%BE/u21.png?pageId=2f9ba10c-92b8-4c9b-b40b-04e65a0b4333'
|
||||
icon: shareIcon
|
||||
},
|
||||
{
|
||||
title: '下载二维码',
|
||||
type: 'qrCode',
|
||||
icon: 'https://files.axshare.com/gsc/DR6075/44/1a/03/441a03a8b1004755a7a392b311acf97f/images/%E6%8A%95%E6%94%BE/u21.png?pageId=2f9ba10c-92b8-4c9b-b40b-04e65a0b4333'
|
||||
icon: downloadIcon
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -87,65 +96,7 @@ interface PublishInfo {
|
||||
|
||||
const publishInfo = ref<PublishInfo>({} as PublishInfo);
|
||||
type OperateItem = (typeof operateList)[0];
|
||||
// function handlePublish() {
|
||||
// publishSurvey({
|
||||
// sn,
|
||||
// publish_type: publishType.value
|
||||
// })
|
||||
// .then(() => {
|
||||
// // store.commit('common/M_COMMON_SET_SURVEY_STATUS', 1);
|
||||
// fetchInfo();
|
||||
// })
|
||||
// .catch(() => {
|
||||
// // emitter.emit('app-loading', false);
|
||||
// });
|
||||
// }
|
||||
|
||||
// async function openPublishModal() {
|
||||
// const res = await canPlanetPublish(route.query.sn);
|
||||
// if (res) {
|
||||
// handlePublish();
|
||||
// } else {
|
||||
// emitter.emit('app-loading', false);
|
||||
// }
|
||||
// }
|
||||
|
||||
function getCode() {
|
||||
publishInfo.value.img_url
|
||||
= 'https://test-cxp-pubcos.yili.com/uat-yls//survey-api/publish/202503130938138261340.png';
|
||||
publishInfo.value.url = `${configUrl.proxyDomain}/publish?sn=${sn && sn !== undefined ? sn : ''}`;
|
||||
publishInfo.value.download_url = {
|
||||
title: '问卷下载',
|
||||
url: 'https://test-cxp-pubcos.yili.com/uat-yls//survey-api/publish/202503130938138261340.png'
|
||||
};
|
||||
getQrcode(sn)
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
publishInfo.value = res.data.data || {};
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
showFailToast(error.data?.message || error.message || '服务器错误');
|
||||
});
|
||||
}
|
||||
// function fetchInfo() {
|
||||
// getSurveyInfo(sn)
|
||||
// .then((res) => {
|
||||
// status.value = Number(res.data.data.status);
|
||||
// })
|
||||
// .finally(() => {
|
||||
// /**/
|
||||
// });
|
||||
// }
|
||||
watch(status, (val) => {
|
||||
if (val === 1) {
|
||||
getCode();
|
||||
}
|
||||
});
|
||||
onMounted(async() => {
|
||||
// fetchInfo();
|
||||
getCode();
|
||||
});
|
||||
const operateBtn = (item: OperateItem) => {
|
||||
switch (item.type) {
|
||||
case 'shareLink':
|
||||
@@ -182,9 +133,7 @@ function shareLink() {
|
||||
scene: 0 // 朋友圈1 微信好友0
|
||||
};
|
||||
console.log('shareUrl', publishInfo.value.url);
|
||||
appBridge.shareToWeChat(params, () => {
|
||||
// 后续扩展
|
||||
});
|
||||
appBridge.shareToWeChat(params);
|
||||
}
|
||||
|
||||
// 下载二维码
|
||||
@@ -203,6 +152,58 @@ function downLoadImg() {
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
}
|
||||
async function openPublishModal() {
|
||||
const res = await canPlanetPublish(route.query.sn as string, publishType.value);
|
||||
if (res) {
|
||||
await publishSurvey({
|
||||
sn,
|
||||
publish_type: publishType.value
|
||||
})
|
||||
.then(() => {
|
||||
fetchInfo();
|
||||
})
|
||||
.catch((error) => {
|
||||
showFailToast(error.data?.message || error.message || '服务器错误');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getCode() {
|
||||
getQrcode(sn)
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
publishInfo.value = res.data.data || {};
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
showFailToast(error.data?.message || error.message || '服务器错误');
|
||||
});
|
||||
}
|
||||
function fetchInfo() {
|
||||
getSurveyInfo(sn)
|
||||
.then((res) => {
|
||||
status.value = Number(res.data.data.status);
|
||||
})
|
||||
.catch((error) => {
|
||||
showFailToast(error.data?.message || error.message || '服务器错误');
|
||||
});
|
||||
}
|
||||
watch(status, (val) => {
|
||||
if (val === 1) {
|
||||
getCode();
|
||||
}
|
||||
});
|
||||
onMounted(async() => {
|
||||
// status.value = 0;
|
||||
// publishInfo.value.img_url
|
||||
// = 'https://test-cxp-pubcos.yili.com/uat-yls//survey-api/publish/202503130938138261340.png';
|
||||
// publishInfo.value.url = `${configUrl.proxyDomain}/publish?sn=${sn && sn !== undefined ? sn : ''}`;
|
||||
// publishInfo.value.download_url = {
|
||||
// title: '问卷下载dadad阿萨法发是否反反复复烦烦烦烦烦阿三大苏打烦烦烦烦',
|
||||
// url: 'https://test-cxp-pubcos.yili.com/uat-yls//survey-api/publish/202503130938138261340.png'
|
||||
// };
|
||||
fetchInfo();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -210,19 +211,11 @@ function downLoadImg() {
|
||||
height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
* {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 3px 20px;
|
||||
border: none;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
* {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const successImg
|
||||
= 'https://files.axshare.com/gsc/DR6075/c7/5a/53/c75a534148d349f1bb8e185629f784ac/images/%E9%A2%84%E8%A7%88/u123.png?pageId=18fb9d8a-b9b7-465f-9bd7-625b1b78f72e';
|
||||
const successImg =
|
||||
'https://files.axshare.com/gsc/DR6075/c7/5a/53/c75a534148d349f1bb8e185629f784ac/images/%E9%A2%84%E8%A7%88/u123.png?pageId=18fb9d8a-b9b7-465f-9bd7-625b1b78f72e';
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||