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

This commit is contained in:
Huangzhe
2025-03-16 15:33:37 +08:00
83 changed files with 3615 additions and 7429 deletions

4
auto-imports.d.ts vendored
View File

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

6
components.d.ts vendored
View File

@@ -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']

View File

@@ -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
View File

@@ -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",

View File

@@ -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
View 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
}
}
}

View File

@@ -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'
});
}

View File

@@ -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
View File

@@ -0,0 +1,8 @@
import request from '@/utils/request.js';
export function uploadDocumentFile(data) {
return request({
url: `/console/document_import`,
method: 'post',
data
});
}

View File

@@ -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:

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

BIN
src/assets/img/empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -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();
}
}
};

View File

@@ -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);
}
// 渲染全部列

View File

@@ -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>

View File

@@ -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
View 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">&#xea15;</span>
<div class="name">橡皮,擦除,橡皮擦</div>
<div class="code-name">&amp;#xea15;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe61f;</span>
<div class="name">画笔</div>
<div class="code-name">&amp;#xe61f;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe6e2;</span>
<div class="name">撤销</div>
<div class="code-name">&amp;#xe6e2;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe613;</span>
<div class="name">上传</div>
<div class="code-name">&amp;#xe613;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe6dc;</span>
<div class="name">清空</div>
<div class="code-name">&amp;#xe6dc;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe71d;</span>
<div class="name">blod</div>
<div class="code-name">&amp;#xe71d;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe71e;</span>
<div class="name">qingxie</div>
<div class="code-name">&amp;#xe71e;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe720;</span>
<div class="name">undline</div>
<div class="code-name">&amp;#xe720;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe730;</span>
<div class="name">image</div>
<div class="code-name">&amp;#xe730;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe637;</span>
<div class="name">del</div>
<div class="code-name">&amp;#xe637;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe600;</span>
<div class="name">more</div>
<div class="code-name">&amp;#xe600;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe66d;</span>
<div class="name">delete</div>
<div class="code-name">&amp;#xe66d;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe6f5;</span>
<div class="name">del</div>
<div class="code-name">&amp;#xe6f5;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe6ff;</span>
<div class="name">option</div>
<div class="code-name">&amp;#xe6ff;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe633;</span>
<div class="name">setting</div>
<div class="code-name">&amp;#xe633;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe686;</span>
<div class="name">add</div>
<div class="code-name">&amp;#xe686;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe75b;</span>
<div class="name">radio_box</div>
<div class="code-name">&amp;#xe75b;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe641;</span>
<div class="name">矩阵打分</div>
<div class="code-name">&amp;#xe641;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe6c3;</span>
<div class="name">checkbox-checked</div>
<div class="code-name">&amp;#xe6c3;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe6b0;</span>
<div class="name">nps</div>
<div class="code-name">&amp;#xe6b0;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe6fd;</span>
<div class="name">input</div>
<div class="code-name">&amp;#xe6fd;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe62e;</span>
<div class="name">矩阵填空</div>
<div class="code-name">&amp;#xe62e;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe631;</span>
<div class="name">文件上传</div>
<div class="code-name">&amp;#xe631;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe661;</span>
<div class="name">签名</div>
<div class="code-name">&amp;#xe661;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe62c;</span>
<div class="name">图文</div>
<div class="code-name">&amp;#xe62c;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe818;</span>
<div class="name">矩阵多选</div>
<div class="code-name">&amp;#xe818;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#x13c7f;</span>
<div class="name">矩阵单选</div>
<div class="code-name">&amp;#x13c7f;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe630;</span>
<div class="name">edit-2</div>
<div class="code-name">&amp;#xe630;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe632;</span>
<div class="name">copy</div>
<div class="code-name">&amp;#xe632;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe63f;</span>
<div class="name">delete</div>
<div class="code-name">&amp;#xe63f;</div>
</li>
<li class="dib">
<span class="icon mobilefont">&#xe6a0;</span>
<div class="name">sort</div>
<div class="code-name">&amp;#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"
>&lt;span class="mobilefont"&gt;&amp;#x33;&lt;/span&gt;
</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">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
</code></pre>
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;span class="mobilefont mobilefont-xxx"&gt;&lt;/span&gt;
</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">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
</code></pre>
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
<pre><code class="language-html">&lt;style&gt;
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
&lt;/style&gt;
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
&lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
&lt;/svg&gt;
</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>

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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';
}

File diff suppressed because one or more lines are too long

View File

@@ -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",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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 {};

View File

@@ -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
View File

@@ -0,0 +1,463 @@
/**
* 问卷设计中涉及到的一些 【逻辑】 中公共方法
*/
import config from '@/config';
import { showDialog } from 'vant';
const advancedQuesTypeList = [
{
name: 'Maxdiff',
icon: '&#xe70c;',
check: false,
type: 105
},
{
name: 'CBC',
icon: '&#xe70d;',
check: false,
type: 103
},
{
name: 'BPTO',
icon: '&#xe70d;',
check: false,
type: 104
},
{
name: 'PSM',
icon: '&#xe708;',
check: false,
type: 101
},
{
name: 'KANO',
icon: '&#xe710;',
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
View File

@@ -0,0 +1,311 @@
const basicQuesTypeList = [
{
name: '选择题',
icon: '&#xe6d3;',
check: false,
type: 1,
childTypes: [1, 2]
},
{
name: '级联题',
icon: '&#xe6b6;',
check: false,
type: 3,
childTypes: [3]
},
{
name: '填空题',
icon: '&#xe6c2;',
check: false,
type: 4,
childTypes: [4]
},
{
name: '打分题',
icon: '&#xe6c1;',
check: false,
type: 5,
childTypes: [5]
},
{
name: '矩阵题',
icon: '&#xe71c;',
check: false,
type: 9,
childTypes: [8, 9, 10, 11]
},
{
name: '图片题',
icon: '&#xe71e;',
check: false,
type: 13,
childTypes: [12, 13, 14]
},
{
name: '分类题',
icon: '&#xe700;',
check: false,
type: 15,
childTypes: [15]
},
{
name: '排序题',
icon: '&#xe71d;',
check: false,
type: 16,
childTypes: [16]
},
{
name: '图文说明题',
icon: '&#xe6d0;',
check: false,
type: 6,
childTypes: [6]
},
{
name: '日期/时间',
icon: '&#xe6ac;',
check: false,
type: 7,
childTypes: [7]
},
{
name: '恒定总和题',
icon: '&#xe6ff;',
check: false,
type: 17,
childTypes: [17]
},
{
name: '文件上传题',
icon: '&#xe6f9;',
check: false,
type: 18,
childTypes: [18]
},
// {
// name: "组合表格题",
// icon: "&#xe6f9;",
// check: false,
// type: 24,
// childTypes: [24],
// },
{
name: '热区题',
icon: '&#xe834;',
check: false,
type: 25,
childTypes: [25, 56]
},
{
name: 'NPS',
icon: '&#xe833;',
check: false,
type: 106,
childTypes: [106]
}
];
const quickQuesTypeList = [
{
name: '姓名',
icon: '&#xe713;',
check: false,
type: 4,
quickType: 6
},
{
name: '性别',
icon: '&#xe716;',
check: false,
type: 1,
quickType: 7
},
{
name: '手机号',
icon: '&#xe638;',
check: false,
type: 20
},
{
name: '身份证号',
icon: '&#xe6ba;',
check: false,
type: 4,
quickType: 2
},
{
name: '邮箱',
icon: '&#xe70b;',
check: false,
type: 4,
quickType: 8
},
{
name: '年龄',
icon: '&#xe701;',
check: false,
type: 4,
quickType: 9
},
{
name: '年龄段',
icon: '&#xe6fb;',
check: false,
type: 1,
quickType: 10
},
{
name: '生日',
icon: '&#xe729;',
check: false,
type: 7,
quickType: 11
},
{
name: '学历',
icon: '&#xe712;',
check: false,
type: 1,
quickType: 12
},
{
name: '院校',
icon: '&#xe718;',
check: false,
type: 3,
quickType: 13
},
{
name: '专业',
icon: '&#xe705;',
check: false,
type: 3,
quickType: 14
},
{
name: '行业',
icon: '&#xe715;',
check: false,
type: 1,
quickType: 15
},
{
name: '地理位置',
icon: '&#xe6fd;',
check: false,
type: 19,
quickType: 16
},
{
name: '省份',
icon: '&#xe6b8;',
check: false,
type: 3,
quickType: 3
},
{
name: '省市',
icon: '&#xe6bd;',
check: false,
type: 3,
quickType: 4
},
{
name: '省市区/县',
icon: '&#xe6cb;',
check: false,
type: 3,
quickType: 5
},
{
type: 21,
name: '密码',
icon: '&#xe639;',
check: false
},
{
type: 22,
name: '签名题',
icon: '&#xe637;',
check: false
},
{
type: 23,
name: '知情同意书',
icon: '&#xe63a;',
check: false
}
];
const advancedQuesTypeList = [
{
name: 'Maxdiff',
icon: '&#xe70c;',
check: false,
type: 105
},
{
name: 'CBC',
icon: '&#xe70d;',
check: false,
type: 103
},
{
name: 'BPTO',
icon: '&#xe70d;',
check: false,
type: 104
},
{
name: 'PSM',
icon: '&#xe708;',
check: false,
type: 101
},
{
name: 'KANO',
icon: '&#xe710;',
check: false,
type: 102
}
];
const d3QuestypeList = [
{
name: 'ID test',
icon: '&#xe762;',
check: false,
type: 200
},
{
name: 'Experiment',
icon: '&#xe765;',
check: false,
type: 201
}
];
const uxSimuatorQuestypeList = [
{
name: 'UI',
icon: '&#xe764;',
check: false,
type: 202
},
{
name: 'Prototype',
icon: '&#xe763;',
check: false,
type: 203
},
{
name: 'UE',
icon: '&#xe761;',
check: false,
type: 204
}
];
module.exports = {
basicQuesTypeList,
quickQuesTypeList,
advancedQuesTypeList,
d3QuestypeList,
uxSimuatorQuestypeList
};

View File

@@ -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 实体 &times; 来替代 ant - design 的 CloseOutlined
// ['&times;']
// ),
// 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',

View File

@@ -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()) {

View File

@@ -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';

View File

@@ -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('保存中...');

View File

@@ -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')
},

View File

@@ -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('保存中...');

View 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);
}
});
});
}

View File

@@ -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;
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 = [];

View File

@@ -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);

View File

@@ -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) => {

View File

@@ -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>

View File

@@ -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;

View File

@@ -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
);
}
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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 }}

View File

@@ -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;

View File

@@ -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>

View File

@@ -11,7 +11,8 @@
class="iconfont active-icon"
:style="{ marginRight: isLastPage ? '0' : '16px' }"
@click="activePage"
>&#xe86c;</i>
>&#xe86c;</i
>
<template v-if="!isLastPage">
<i class="iconfont moverQues" style="margin-right: 16px">&#xe71b;</i>
<i class="iconfont" @click="deleteHandle">&#xe6c5;</i>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -106,8 +106,8 @@ onMounted(() => {
}
.more {
margin: 20px auto;
color: #000;
margin: 20px;
color: #919191;
font-weight: 400;
font-size: 13px;
text-align: center;

View File

@@ -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,

View File

@@ -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

View File

@@ -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>

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);
};

View File

@@ -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)
);
});

View File

@@ -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;
}

View File

@@ -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) => {

View File

@@ -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: {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 {

View File

@@ -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>