Compare commits
133 Commits
uat
...
feature/se
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5642d0d3d | ||
|
|
acc02c5a75 | ||
|
|
ec278a03b7 | ||
|
|
49cbb071e8 | ||
|
|
f9775f4a57 | ||
|
|
281fee561c | ||
|
|
a62039d851 | ||
|
|
6a09aef7a7 | ||
|
|
73e1c89f5d | ||
|
|
9462843e6a | ||
|
|
7502c2c800 | ||
|
|
520197e9b8 | ||
|
|
a6adf8dab7 | ||
|
|
2d28c9efcb | ||
|
|
4982e283a8 | ||
|
|
0173b0b05e | ||
|
|
b1fb42668c | ||
|
|
dc63cec92a | ||
|
|
8f06726bb6 | ||
|
|
af323f2c74 | ||
|
|
e89f554830 | ||
|
|
966e56ad74 | ||
|
|
f4f84e0b68 | ||
|
|
e3269c555d | ||
|
|
880c67b451 | ||
|
|
1de9b5b539 | ||
|
|
4521f0b443 | ||
|
|
4a6d277b32 | ||
|
|
ba0dae323a | ||
|
|
fedfab75be | ||
|
|
7c5b9e460a | ||
|
|
3fa7b6e2e1 | ||
|
|
6f11b91849 | ||
|
|
5ab49b7b66 | ||
|
|
5bcc6db202 | ||
|
|
1181dc0f52 | ||
|
|
0084ed80d0 | ||
|
|
7822ce4516 | ||
|
|
92a28bca1e | ||
|
|
8561011f9b | ||
|
|
88da1cd002 | ||
|
|
12b25d8148 | ||
|
|
8415818f28 | ||
|
|
9afd82d9d7 | ||
|
|
05646a31ae | ||
|
|
a121e2ce81 | ||
|
|
544673c350 | ||
|
|
09ab4901d8 | ||
|
|
709f56ac74 | ||
|
|
93bb2b3594 | ||
|
|
e543896c2a | ||
|
|
fced5acf2f | ||
|
|
1549dc704d | ||
|
|
d49f856fd0 | ||
|
|
73841a2d07 | ||
|
|
f6be0143bc | ||
|
|
5b79337c39 | ||
|
|
4d13a28eb2 | ||
|
|
e39982c6f5 | ||
|
|
13d30a51a8 | ||
|
|
94330b1f01 | ||
|
|
44bf17d47b | ||
|
|
10a4904e26 | ||
|
|
079a589510 | ||
|
|
3f0bc59a0a | ||
|
|
965f4df493 | ||
|
|
7650402cc2 | ||
|
|
1322f26167 | ||
|
|
9d478ce108 | ||
|
|
0f1e1839da | ||
|
|
55cfd96601 | ||
|
|
48bab50a7e | ||
|
|
d9187a0694 | ||
|
|
b63e336c08 | ||
|
|
6eed4bf53e | ||
|
|
74ad7a19eb | ||
|
|
c073cd38f2 | ||
|
|
1ec224181d | ||
|
|
1ec680ef16 | ||
|
|
623ffb13e3 | ||
|
|
af8b2765bc | ||
|
|
0dd4600d35 | ||
|
|
91d1cbc21b | ||
|
|
cf0eb9a57c | ||
|
|
2b297d5e80 | ||
|
|
1cf2ad8155 | ||
|
|
a2fbf185a1 | ||
|
|
e587ed96b2 | ||
|
|
d58df5727a | ||
|
|
b7c7d6b214 | ||
|
|
96495f268c | ||
|
|
92410be2e0 | ||
|
|
415d7117c3 | ||
|
|
37c0cff959 | ||
|
|
e2cc6adc19 | ||
|
|
8ecb195fca | ||
|
|
450dec6c86 | ||
|
|
09efcc851e | ||
|
|
44a1f39b82 | ||
|
|
ef854a51f8 | ||
|
|
633f3c74c4 | ||
|
|
65c783ff02 | ||
|
|
03c9154602 | ||
|
|
beb74bb3b0 | ||
|
|
bd922cffd5 | ||
|
|
c5e424b256 | ||
|
|
e392629356 | ||
|
|
b938f48c50 | ||
|
|
345c80acbc | ||
|
|
18b4f26554 | ||
|
|
8fa1059bd7 | ||
|
|
a0a03e948e | ||
|
|
468d7de19b | ||
|
|
81a1e5b127 | ||
|
|
c4c00134e7 | ||
|
|
f5f757b66f | ||
|
|
f886a51110 | ||
|
|
0a91df64b5 | ||
|
|
216b443540 | ||
|
|
712985b643 | ||
|
|
41850e5187 | ||
|
|
1043126c72 | ||
|
|
dec8ec0ec0 | ||
|
|
001279874d | ||
|
|
23b1a46bec | ||
|
|
f9b5d001e5 | ||
|
|
83aee0d18a | ||
|
|
76b5235ab5 | ||
|
|
364b00f51f | ||
|
|
f83e910159 | ||
|
|
51676a39b4 | ||
|
|
36d3df3e80 | ||
|
|
2ee26e3e5b |
18
.env.prod
Normal file
@@ -0,0 +1,18 @@
|
||||
# .env.development
|
||||
VITE_APP_BASEURL=https://yls.xapi.digitalyili.com/
|
||||
VITE_APP_BASEDOMAIM=https://ylst-h5-uat.dctest.digitalyili.com
|
||||
# VITE_APP_BASEURL=http://192.168.8.165:15011
|
||||
VITE_APP_ENV=production
|
||||
VITE_APP_CURRENTMODE=prod
|
||||
VITE_APP_BASEOSS=https://cxp-pubcos.yili.com/prod-yls
|
||||
VITE_APP_DELIVERY_BASEURL=https://ylsdist-net.x.digitalyili.com/
|
||||
VITE_APP_MESSAGE_CENTER=http://gtech-gateway.cxpin.digitalyili.com/apigtech/message-send-center/
|
||||
|
||||
VITE_APP_SOCKETURL=wss://yls.xapi.digitalyili.com/survey_sync
|
||||
VITE_APP_JSONPURL=https://iam.digitalyili.com/idp/restful/getIDPToken
|
||||
VITE_APP_YQRURL=https://ocp.digitalyili.com
|
||||
|
||||
# VITE_APP_BASE_APPURL=https://ycsb-gw-uat.dcin-test.digitalyili.com
|
||||
# VITE_APP_APPKEY=62f495a0f7854e4e46ebbf40
|
||||
# VITE_APP_APPID=m5c66hlce3
|
||||
VITE_APP_AIAGENTURL=/aiagent/assistant/1179e9d7-fb94-4fe4-abbb-55be8f8ca5d6/share
|
||||
1
.env.uat
@@ -31,3 +31,4 @@ VUE_APP_JSONPURL = 'https://iam-uat.dctest.digitalyili.com/idp/restful/getIDPTok
|
||||
|
||||
VUE_APP_YQRURL = 'https://ocp-uat-ain.digitalyili.com'
|
||||
|
||||
VITE_APP_AIAGENTURL=/aiagent/assistant/78907182-cc42-4072-abae-86ef67c1ecd3/share
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM yldc-docker.pkg.coding.yili.com/pubrepo/pubdocker/nginx:latest
|
||||
FROM yldc-docker.pkg.coding.yili.com/pubrepo/pubdocker/nginx:op
|
||||
|
||||
# ---------------------只改此处,其它不动--------------------------
|
||||
# 定义构建物目录 当前目录 ./ 为代码根目录
|
||||
@@ -14,6 +14,6 @@ COPY ${DIST_DIR} /var/www/html/${URL_DIR}
|
||||
RUN sed -i "s/\ \/index.html/\ \/${URL_DIR}\/index.html/g" /etc/nginx/conf.d/default.conf
|
||||
|
||||
# 默认不建议使用自定义nginx配置,如必须使用,请提前沟通
|
||||
# COPY ./nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY ./docker/default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 80
|
||||
|
||||
43
docker/default.conf
Normal file
@@ -0,0 +1,43 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
|
||||
#charset koi8-r;
|
||||
#access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /var/www/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ //index.html;
|
||||
}
|
||||
|
||||
# 启用访问日志,并指定日志文件路径和格式
|
||||
access_log /var/log/nginx/host.access.log;
|
||||
|
||||
# 打印错误日志文件信息
|
||||
error_log /var/log/nginx/host.error.log;
|
||||
location /yllog {
|
||||
root /var/log/nginx;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
location /aiagent/ {
|
||||
proxy_pass https://yiligpt.x.digitalyili.com;
|
||||
proxy_set_header Host yiligpt.x.digitalyili.com;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# 定义日志格式(通常在 http 块中定义即可)
|
||||
# log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
# '$status $body_bytes_sent "$http_referer" '
|
||||
# '"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /var/www/html;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,8 +6,10 @@
|
||||
"scripts": {
|
||||
"dev": "vite --mode development",
|
||||
"uat": "vite --mode uat",
|
||||
"prod": "vite --mode prod",
|
||||
"build": "run-p \"build-only {@}\" --",
|
||||
"build_uat": "vite build --mode uat",
|
||||
"build_prod": "vite build --mode prod",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"lint": "npx eslint -c ./node_modules/@yl/yili-fe-lint-config/eslintrc.vue3.js \"src/**/*.{js,ts,tsx,vue,html}\" --fix",
|
||||
|
||||
BIN
public/yl.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
@@ -1,6 +1,6 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--primary-color: #70B937;
|
||||
--primary-color: #70b937;
|
||||
--van-primary-color: #71b73c;
|
||||
--vt-c-white: #fff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
@@ -45,6 +45,7 @@
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
--section-gap: 160px;
|
||||
--el-select-input-focus-border-color: var(--primary-color);
|
||||
--el-color-primary: var(--primary-color);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
@@ -51,7 +51,7 @@ a,
|
||||
top: 0;
|
||||
width: 100%;
|
||||
// background-color: linear-gradient(to bottom, theme.$theme-color 200px, theme.$nav-color 300px);
|
||||
background: url("../img/home/nav.png") theme.$nav-color;
|
||||
background: url('../img/home/nav.png') theme.$nav-color;
|
||||
color: #000;
|
||||
|
||||
& .van-nav-bar__content {
|
||||
|
||||
@@ -300,3 +300,12 @@ input {
|
||||
background: linear-gradient(180deg, #e8fad7 0%, #ffffff 8%);
|
||||
}
|
||||
//background: linear-gradient( 180deg, #E8FAD7 0%, #FFFFFF 100%);
|
||||
|
||||
.el-input-number__decrease,
|
||||
.el-input-number__increase {
|
||||
display: none;
|
||||
}
|
||||
.el-input-number .el-input__wrapper,
|
||||
.el-input-number.is-controls-right .el-input__wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
BIN
src/assets/img/icon_navigate_back.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/img/market/11.png
Normal file
|
After Width: | Height: | Size: 665 B |
BIN
src/assets/img/market/13.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/img/market/15.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
src/assets/img/market/16.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
src/assets/img/market/17.png
Normal file
|
After Width: | Height: | Size: 706 B |
BIN
src/assets/img/market/18.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 17 KiB |
@@ -1,34 +1,35 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, useTemplateRef, watch } from 'vue';
|
||||
import { computed, nextTick, onErrorCaptured, ref, useTemplateRef, watch } from 'vue';
|
||||
import { useSetPieChart } from '@/hooks/chart/usePieChart';
|
||||
import {
|
||||
formatData,
|
||||
getTableData,
|
||||
setDimensionData
|
||||
} from '@/views/Survey/views/Analysis/components/AnalysisInfo/hooks/pieSeries';
|
||||
import EmptyContainer from '@/views/Survey/components/EmptyContainer.vue';
|
||||
|
||||
// series 信息
|
||||
const tableData = ref([]);
|
||||
const analysis = defineModel<any>('analysis');
|
||||
|
||||
// console.log('analysis', analysis.value);
|
||||
|
||||
const pieChart = useTemplateRef<HTMLSpanElement>('pieChart');
|
||||
// series 信息
|
||||
const series = ref([]);
|
||||
const dimension = defineModel('dimension');
|
||||
// 图标高度
|
||||
const chartHeight = computed(() => {
|
||||
// 需要增加的高度, 由于字多了他就会高度异常,所有要指定一个动态高度
|
||||
let addHeight = 0;
|
||||
const optionLength = analysis.value?.option?.length;
|
||||
// const optionLength = analysis.value?.option?.length;
|
||||
|
||||
// 做一些额外的检测, 如果 option 下面的title 字段超过 8 个,就把 addHeight 增加
|
||||
analysis.value?.option?.forEach((item: any) => {
|
||||
if (item.title?.length > 8) {
|
||||
addHeight += 9;
|
||||
addHeight += 2;
|
||||
}
|
||||
});
|
||||
|
||||
// 每三个选项高度增加 20px, 默认 300px
|
||||
return dimension.value ? 280 : 280 + (optionLength - 1) * addHeight;
|
||||
return dimension.value ? 280 : 280 + addHeight;
|
||||
});
|
||||
|
||||
const index = ref(0);
|
||||
@@ -37,21 +38,22 @@ const index = ref(0);
|
||||
watch(
|
||||
() => analysis.value,
|
||||
async (value) => {
|
||||
// 排除空数据渲染图标步骤
|
||||
|
||||
tableData.value = {
|
||||
...analysis.value,
|
||||
option: getTableData(analysis.value)
|
||||
};
|
||||
|
||||
// console.log(`图标的高度是`, chartHeight.value);
|
||||
// console.log(`tableData.value`, tableData.value);
|
||||
|
||||
series.value = formatData(dimension.value ? tableData.value : analysis.value, index.value);
|
||||
const pieChart = useTemplateRef<HTMLSpanElement>('pieChart');
|
||||
// console.log(`series value `, series.value);
|
||||
// console.log(`series value`, series.value);
|
||||
|
||||
if (!series.value?.data?.length) return;
|
||||
if (!series.value.data?.length) return;
|
||||
// nextTick(() => {
|
||||
// setTimeout(() => {
|
||||
useSetPieChart(pieChart, series, { title: false, legend: false });
|
||||
// }, 1000);
|
||||
// });
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
@@ -60,12 +62,53 @@ const changeChart = (i: number) => {
|
||||
index.value = i;
|
||||
|
||||
series.value = formatData(tableData.value, index.value);
|
||||
// console.log(`series value. by changeChart`, series.value);
|
||||
|
||||
if (series.value.data.length <= 0) {
|
||||
series.value.data = [{ value: 0, name: 0 }];
|
||||
}
|
||||
// const pieChart = useTemplateRef<HTMLSpanElement>('pieChart');
|
||||
// useSetPieChart(pieChart, series, { title: false, legend: false });
|
||||
};
|
||||
|
||||
const chartVisible = computed(() => {
|
||||
/**
|
||||
* 针对特殊题型做处理,
|
||||
* 首先就是矩阵的题目 question_type 为 8 9 10
|
||||
*
|
||||
* */
|
||||
|
||||
if (
|
||||
analysis.value.question_type === 8 ||
|
||||
analysis.value.question_type === 9 ||
|
||||
analysis.value.question_type === 10
|
||||
) {
|
||||
// console.log(`series.value on matrix`, series.value?.data);
|
||||
const data = series.value?.data as { name: any; value: any }[];
|
||||
|
||||
// 过滤后的 data 数据,
|
||||
const filterData = data.filter((item) => item.value != 0 && item.name != 0);
|
||||
return filterData.length;
|
||||
// series.value?.data.forEach((item: any`1[]) => {
|
||||
// if (item.value > 0) {
|
||||
// return true;
|
||||
// }
|
||||
// });
|
||||
} else if (analysis.value.question_type === 106 || analysis.value.question_type === 5) {
|
||||
const data = series.value?.data as { name: any; value: any }[];
|
||||
const filterData = data?.filter((item) => item.value && Number(item.value) > 0);
|
||||
|
||||
return filterData?.length;
|
||||
// console.log(`series.value on nps`, series.value?.data);
|
||||
}
|
||||
|
||||
return series.value?.data?.length;
|
||||
});
|
||||
|
||||
onErrorCaptured((err) => {
|
||||
console.log(`err`, err);
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -85,7 +128,7 @@ const changeChart = (i: number) => {
|
||||
|
||||
<!-- 图表部分 -->
|
||||
<div
|
||||
v-if="series?.data?.length"
|
||||
v-if="chartVisible"
|
||||
class="charts"
|
||||
:style="{
|
||||
display: 'flex',
|
||||
@@ -98,10 +141,14 @@ const changeChart = (i: number) => {
|
||||
ref="pieChart"
|
||||
:style="{
|
||||
width: '100%',
|
||||
height: series?.data?.length ? chartHeight + 'px' : ''
|
||||
height: chartVisible ? chartHeight + 'px' : ''
|
||||
}"
|
||||
></span>
|
||||
</div>
|
||||
<!-- 图标没有实例就开始展示空图标 -->
|
||||
<div v-else>
|
||||
<empty-container :error-msg="'本题暂无有效答题数据'" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -10,11 +10,6 @@
|
||||
<div class="content">
|
||||
<div class="title fw-bold fs-14">
|
||||
<div class="flex align-center">
|
||||
<img
|
||||
:src="setImg(item.scene_code_info)"
|
||||
alt="Content Icon"
|
||||
style="width: 15px; height: 15px"
|
||||
/>
|
||||
<p class="title_con">{{ item.title }}</p>
|
||||
</div>
|
||||
<van-icon
|
||||
@@ -54,14 +49,6 @@ import { defineProps, onMounted, ref } from 'vue';
|
||||
import { deleteTemplate } from '@/api/home/index.js';
|
||||
import { showDialog, showFailToast, showSuccessToast } from 'vant';
|
||||
import { useRouter } from 'vue-router';
|
||||
import png11 from '@/assets/img/home/11.png';
|
||||
import png13 from '@/assets/img/home/13.png';
|
||||
import png14 from '@/assets/img/home/14.png';
|
||||
import png15 from '@/assets/img/home/15.png';
|
||||
import png16 from '@/assets/img/home/16.png';
|
||||
import png17 from '@/assets/img/home/17.png';
|
||||
import png18 from '@/assets/img/home/18.png';
|
||||
import png31 from '@/assets/img/home/31.png';
|
||||
|
||||
const router = useRouter();
|
||||
const { info, marketItem } = defineProps({
|
||||
@@ -77,7 +64,8 @@ const { info, marketItem } = defineProps({
|
||||
});
|
||||
const userInfo = ref({ userName: '' });
|
||||
const toDetail = (item) => {
|
||||
// return false
|
||||
const marketList = JSON.parse(sessionStorage.getItem('marketList'));
|
||||
const marketItem = marketList.find((market) => item.scene_code_info === market.code);
|
||||
router.push({
|
||||
path: '/templatePreview',
|
||||
query: {
|
||||
@@ -111,20 +99,6 @@ const deleteItem = (item) => {
|
||||
// on cancel
|
||||
});
|
||||
};
|
||||
const setImg = (code) => {
|
||||
const imageMap = {
|
||||
11: png11,
|
||||
13: png13,
|
||||
14: png14,
|
||||
15: png15,
|
||||
16: png16,
|
||||
17: png17,
|
||||
18: png18,
|
||||
31: png31
|
||||
};
|
||||
// 默认返回 png11 如果 code 不存在
|
||||
return imageMap[code] || png11;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
userInfo.value = JSON.parse(sessionStorage.getItem('userInfo'));
|
||||
|
||||
@@ -17,14 +17,14 @@ const navigation = ref([
|
||||
icon: 'home-o',
|
||||
inactive: ''
|
||||
},
|
||||
{
|
||||
title: '伊调研',
|
||||
// link: {
|
||||
// name: 'home',
|
||||
// path: '/home'
|
||||
// },
|
||||
icon: yl
|
||||
},
|
||||
// {
|
||||
// title: '伊调研',
|
||||
// // link: {
|
||||
// // name: 'home',
|
||||
// // path: '/home'
|
||||
// // },
|
||||
// icon: yl
|
||||
// },
|
||||
{
|
||||
title: '我的',
|
||||
link: {
|
||||
@@ -65,29 +65,31 @@ watch(activeTab, (value) => {
|
||||
|
||||
<template>
|
||||
<van-tabbar v-model="activeTab" class="navigation">
|
||||
<template v-for="(item, index) in navigation" :key="item.title">
|
||||
<span
|
||||
v-if="index === 1"
|
||||
style="
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<img :src="item.icon" alt="" style="width: 30px; height: 30px" />
|
||||
{{ item.title }}
|
||||
</span>
|
||||
<van-tabbar-item v-else :icon="item.icon" :name="item.title">
|
||||
{{ item.title }}
|
||||
</van-tabbar-item>
|
||||
</template>
|
||||
<div class="nav-content">
|
||||
<template v-for="(item, index) in navigation" :key="item.title">
|
||||
<!-- <span
|
||||
v-if="index === 1"
|
||||
style="
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
"
|
||||
>
|
||||
<img :src="item.icon" alt="" style="width: 30px; height: 30px" />
|
||||
{{ item.title }}
|
||||
</span> -->
|
||||
<van-tabbar-item :icon="item.icon" :name="item.title">
|
||||
{{ item.title }}
|
||||
</van-tabbar-item>
|
||||
</template>
|
||||
</div>
|
||||
</van-tabbar>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.navigation {
|
||||
padding: 3px 0;
|
||||
padding: 10px 0;
|
||||
// width: 100vw;
|
||||
// background-color: white;
|
||||
// display: flex;
|
||||
@@ -97,6 +99,11 @@ watch(activeTab, (value) => {
|
||||
// bottom: 0px;
|
||||
// z-index: 10;
|
||||
}
|
||||
.nav-content {
|
||||
display: flex;
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.navigation-item {
|
||||
display: flex;
|
||||
|
||||
@@ -168,6 +168,7 @@ defineExpose({
|
||||
<!-- :navigation="navigationConfig" -->
|
||||
<swiper-container
|
||||
ref="swiperRef"
|
||||
:autoHeight="true"
|
||||
:slides-per-view="slidesPerView"
|
||||
:space-between="spaceBetween"
|
||||
:centered-slides="centeredSlides"
|
||||
@@ -255,7 +256,7 @@ defineExpose({
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.15);
|
||||
color: #409eff;
|
||||
color: #71b73c;
|
||||
}
|
||||
|
||||
.yl-swiper-pagination {
|
||||
@@ -278,7 +279,7 @@ defineExpose({
|
||||
transition: all 0.3s;
|
||||
|
||||
&.is-active {
|
||||
background-color: #409eff;
|
||||
background-color: #71b73c;
|
||||
width: 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -106,6 +106,26 @@ function tooptipFormatter(data: { row: any; column: any; cellValue: any }): VNod
|
||||
content: data
|
||||
});
|
||||
}
|
||||
|
||||
// render
|
||||
const RenderSlot = {
|
||||
functional: true,
|
||||
props: {
|
||||
row: Object,
|
||||
render: Function,
|
||||
index: Number,
|
||||
column: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
return () => {
|
||||
const { row, index, column } = props;
|
||||
return props.render(h, { row, index, column });
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -131,15 +151,22 @@ function tooptipFormatter(data: { row: any; column: any; cellValue: any }): VNod
|
||||
:width="item.width"
|
||||
:prop="item.prop"
|
||||
:label="item.label"
|
||||
show-overflow-tooltip
|
||||
:show-overflow-tooltip="item.tooltip ? item.tooltip : true"
|
||||
:sortable="item.sortable"
|
||||
>
|
||||
<template #default="scope">
|
||||
<slot name="column-default" :scope="scope">
|
||||
<div
|
||||
class="table-view-html"
|
||||
@click.stop="handleImageClick"
|
||||
v-html="scope.row[item.prop]"
|
||||
></div>
|
||||
<div v-if="!item.render" @click.stop="handleImageClick">
|
||||
<span>{{ scope.row[item.prop] }}</span>
|
||||
</div>
|
||||
|
||||
<RenderSlot
|
||||
v-else
|
||||
:row="scope.row"
|
||||
:render="item.render"
|
||||
:index="scope.$index"
|
||||
:column="item"
|
||||
></RenderSlot>
|
||||
</slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -148,9 +175,14 @@ function tooptipFormatter(data: { row: any; column: any; cellValue: any }): VNod
|
||||
</template>
|
||||
<style>
|
||||
.table-view-html {
|
||||
display: flex;
|
||||
//align-items: center;
|
||||
flex-direction: row;
|
||||
|
||||
& img {
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -165,9 +197,16 @@ function tooptipFormatter(data: { row: any; column: any; cellValue: any }): VNod
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
:deep(.cell),
|
||||
:deep(.table-view-html) {
|
||||
.table-view-html {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.table-col {
|
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
// font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,13 +4,13 @@ const content = defineModel<{ row: any; column: any; cellValue: any }>('content'
|
||||
|
||||
<template>
|
||||
<div class="tooltip-content">
|
||||
<span v-html="content?.row?.logic_text"></span>
|
||||
<span v-html="content?.cellValue"></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tooltip-content {
|
||||
width: 90vw;
|
||||
max-width: 80vw;
|
||||
max-height: 45vh;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
139
src/components/YlTable/yl-table-h.vue
Normal file
@@ -0,0 +1,139 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { showImagePreview } from 'vant';
|
||||
|
||||
const prop = defineModel<any[]>('prop');
|
||||
const data = defineModel<any[]>('data');
|
||||
const tableWidth = ref(0);
|
||||
const doc_content = ref();
|
||||
let startX = 0;
|
||||
const isHorizontal = ref(false);
|
||||
|
||||
function handleTouchStart(event: TouchEvent) {
|
||||
startX = event.touches[0].clientX;
|
||||
isHorizontal.value = false;
|
||||
}
|
||||
function handleImageClick(e: Event) {
|
||||
if (e.target instanceof HTMLImageElement) {
|
||||
const imgSrc = e.target.src;
|
||||
showImagePreview({
|
||||
images: [imgSrc],
|
||||
closeable: true
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleTouchMove(event: TouchEvent) {
|
||||
const deltaX = event.touches[0].clientX - startX;
|
||||
|
||||
// 如果是横向滑动,则阻止冒泡
|
||||
if (Math.abs(deltaX) > 10) {
|
||||
isHorizontal.value = true;
|
||||
event.stopPropagation(); // 阻止事件冒泡到 van-swipe
|
||||
}
|
||||
}
|
||||
watch(
|
||||
() => data.value,
|
||||
(newVal) => {
|
||||
let width = document.getElementsByClassName('doc_content')[0]?.clientWidth
|
||||
? document.getElementsByClassName('doc_content')[0]?.clientWidth
|
||||
: document.body.clientWidth;
|
||||
tableWidth.value = prop.value.length * 100;
|
||||
if (prop.value.length <= 3) {
|
||||
// tableWidth.value 判断是否铺满横屏 没有就铺满
|
||||
tableWidth.value = width;
|
||||
} else {
|
||||
tableWidth.value = width - prop.value.length * 100 > 0 ? width : prop.value.length * 100;
|
||||
}
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
style="overflow: auto; width: 100%"
|
||||
ref="doc_content"
|
||||
@touchstart="handleTouchStart"
|
||||
@touchmove="handleTouchMove"
|
||||
class="doc_content"
|
||||
>
|
||||
<div
|
||||
:style="{
|
||||
width: tableWidth + 'px',
|
||||
overflow: 'auto'
|
||||
}"
|
||||
>
|
||||
<table style="width: 100%; text-align: center; border: none" class="el-table">
|
||||
<thead class="tableThead el-table__header">
|
||||
<tr>
|
||||
<th v-for="item in prop" class="el-table__cell">
|
||||
<div class="cell" style="font-size: 13px">{{ item.label }}</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr v-for="(item, index) in data" :key="index">
|
||||
<template v-for="(t, index) in prop">
|
||||
<td :key="index" v-if="item[index] !== '未知行'" class="el-table__cell">
|
||||
<div
|
||||
class="cell table-view-html"
|
||||
v-html="item[t.prop]"
|
||||
v-if="item[index] !== '未知行'"
|
||||
@click="handleImageClick"
|
||||
></div>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
.table-view-html {
|
||||
img {
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.el-table--fit .el-table__inner-wrapper:before {
|
||||
width: 0;
|
||||
}
|
||||
.el-table__empty-text {
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="scss">
|
||||
.tableThead {
|
||||
background: #f2f8ee;
|
||||
font-size: 12px;
|
||||
|
||||
& tr {
|
||||
background: #f2f8ee;
|
||||
font-size: 12px;
|
||||
& th {
|
||||
background: #f2f8ee;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse; // 合并边框
|
||||
border-spacing: 0; // 清除默认间距
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
& tr {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
//.table-view-html {
|
||||
// display: flex;
|
||||
// //align-items: center;
|
||||
// flex-direction: row;
|
||||
//
|
||||
//
|
||||
//}
|
||||
</style>
|
||||
22
src/hooks/chart/data/bar.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
export const barOption = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
legend: {},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: []
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: [],
|
||||
type: 'bar'
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -17,11 +17,17 @@ function useSetPieChart(
|
||||
let chartInstance: any;
|
||||
|
||||
onMounted(() => {
|
||||
// console.log(`1233313123`,dom.value, series.value);
|
||||
|
||||
// 检测边界范围 dom 和 series 是否存在
|
||||
if (!dom.value && !series) return;
|
||||
if (!dom.value || !series.value) return;
|
||||
|
||||
// 在 dom 挂载之后,显示饼图
|
||||
chartInstance = chart.init(dom.value);
|
||||
// 在 dom 挂载之后,显示饼图
|
||||
if (!chartInstance) {
|
||||
chartInstance = chart.init(dom.value);
|
||||
}
|
||||
// chartInstance = chartInstance ? chartInstance : chart.init(dom.value);
|
||||
pieOption.series = JSON.parse(JSON.stringify(series.value));
|
||||
// 设置图表选项
|
||||
chartInstance.setOption(pieOption, opts);
|
||||
|
||||
17
src/hooks/request/useQuestion.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { getQuestionList } from "@/api/survey";
|
||||
import { ref } from "vue";
|
||||
|
||||
function fetchSingleQuestion(sn: string) {
|
||||
const list = ref([])
|
||||
getQuestionList(sn).then(({ data }) => {
|
||||
list.value = data.data;
|
||||
});
|
||||
return {
|
||||
list,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export {
|
||||
fetchSingleQuestion
|
||||
}
|
||||
@@ -12,7 +12,8 @@
|
||||
@click-left="goBack"
|
||||
>
|
||||
<template #left>
|
||||
<van-icon name="left-long" class-prefix="mobilefont" size="18" style="color: #000" />
|
||||
<img src="@/assets/img/icon_navigate_back.png" class="back-icon" alt="" />
|
||||
<!-- <van-icon name="left-long" class-prefix="mobilefont" size="18" style="color: #000" />-->
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
</header>
|
||||
@@ -85,8 +86,8 @@ function goBack() {
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- title 标题和搜索栏 -->
|
||||
<header class="header">
|
||||
<van-nav-bar
|
||||
:class="[$route.meta.pureBGC ? '' : 'navbar-header']"
|
||||
:class="redirectHeaderStyle"
|
||||
:title="$route.meta.title"
|
||||
left-arrow
|
||||
safe-area-inset-top
|
||||
@@ -11,31 +11,64 @@
|
||||
@click-left="goBack"
|
||||
>
|
||||
<template #left>
|
||||
<van-icon name="left-long" class-prefix="mobilefont" size="18" style="color: #000" />
|
||||
<img src="@/assets/img/icon_navigate_back.png" class="back-icon" alt="" />
|
||||
<!-- <van-icon name="left-long" class-prefix="mobilefont" size="18" style="color: #000" />-->
|
||||
</template>
|
||||
<template #right v-if="$route.meta.shareAction">
|
||||
<el-icon
|
||||
class="nav-layout-right"
|
||||
@click="route.meta.shareAction && route.meta.shareFunc()"
|
||||
><RiShareForwardBoxFill
|
||||
/></el-icon>
|
||||
style="font-size: 30px; width: 30px; height: 30px"
|
||||
>
|
||||
<RiShareForwardBoxFill style="width: 30px; height: 30px" />
|
||||
</el-icon>
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
</header>
|
||||
<!-- content -->
|
||||
<div class="redirect-body" :style="{ background: $route.meta.pureBGC ? 'white' : '' }">
|
||||
<div class="redirect-body" :style="redirectBodyStyle">
|
||||
<RouterView />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
<script setup lang="ts">
|
||||
import { RiShareForwardBoxFill } from 'vue-icons-plus/ri';
|
||||
import { RouterView, useRoute } from 'vue-router';
|
||||
import appBridge from '@/assets/js/appBridge';
|
||||
import { computed } from 'vue';
|
||||
import type { CSSProperties } from 'vue';
|
||||
|
||||
const route = useRoute();
|
||||
console.log(route.meta.pureBGC);
|
||||
// console.log(route.meta.pureBGC);
|
||||
/**
|
||||
* 控制 body 的 style
|
||||
*/
|
||||
const redirectBodyStyle = computed(() => {
|
||||
// 背景色控制
|
||||
const bgc = route.meta.bgc as string;
|
||||
// 是否纯色背景
|
||||
const pureBGC = route.meta.pureBGC;
|
||||
// style 样式
|
||||
const style = {} as CSSProperties;
|
||||
|
||||
if (bgc) style.background = bgc;
|
||||
else if (pureBGC) style.background = '#fff';
|
||||
|
||||
return style;
|
||||
});
|
||||
|
||||
/**
|
||||
* 控制 header 的 style
|
||||
*/
|
||||
const redirectHeaderStyle = computed(() => {
|
||||
let css = [];
|
||||
if (route.meta.header?.bgc === 'green') css.push('green-header');
|
||||
else if (route.meta.header?.pureBGC) {
|
||||
console.log('pureBGC');
|
||||
} else css.push('navbar-header');
|
||||
return css;
|
||||
});
|
||||
|
||||
function goBack() {
|
||||
if (window.history.length > 1 && route.name !== 'home') {
|
||||
@@ -72,11 +105,21 @@ const handlePopState = () => {
|
||||
color: #333;
|
||||
|
||||
.nav-layout-right {
|
||||
:deep(svg) {
|
||||
width: 20px !important;
|
||||
height: 20px !important;
|
||||
stroke-width: 1px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.green-header {
|
||||
background-color: theme.$theme-color;
|
||||
}
|
||||
|
||||
.redirect-body {
|
||||
overflow: scroll;
|
||||
width: 100vw;
|
||||
height: calc(100vh - var(--sticky-top-height) - 1px);
|
||||
background: transparent;
|
||||
}
|
||||
@@ -94,8 +137,8 @@ const handlePopState = () => {
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
34
src/main.ts
@@ -1,7 +1,7 @@
|
||||
import 'amfe-flexible';
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
import { createApp } from 'vue';
|
||||
import { createApp, inject } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
@@ -12,29 +12,49 @@ import appBridge from '@/assets/js/appBridge';
|
||||
import VConsole from 'vconsole';
|
||||
import './assets/css/main.scss';
|
||||
// 引入 swiper 样式
|
||||
import 'swiper/css';
|
||||
import 'swiper/css';
|
||||
import 'swiper/css/navigation';
|
||||
import 'swiper/css/pagination';
|
||||
import { sensorsData } from '@/utils/plugins/sa';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
if (import.meta.env.VITE_APP_ENV !== 'production') {
|
||||
const vconsole = new VConsole();
|
||||
/* const vconsole = */ new VConsole();
|
||||
// app.use(vconsole);
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
sa: any;
|
||||
onAndroidBack: (() => void) | null;
|
||||
appBridge?: any;
|
||||
}
|
||||
}
|
||||
|
||||
const sa = {
|
||||
register: false,
|
||||
instance: window.sa || null
|
||||
};
|
||||
|
||||
// 定义路由是否可以返回的判断
|
||||
const routerCanGoBack = () => {
|
||||
const position = router.options.history.state?.position;
|
||||
return typeof position === 'number' && position > 0;
|
||||
};
|
||||
router.beforeEach((to, from, next) => {
|
||||
// 神策数据埋点
|
||||
if (!sa.register && sessionStorage.getItem('userInfo')) {
|
||||
sa.instance = window.sa;
|
||||
// 检测是否使用神策的登陆
|
||||
const userInfo = JSON.parse(sessionStorage.getItem('userInfo') || '');
|
||||
sa.instance.setOnceProfile({ loginID: userInfo.userCode });
|
||||
|
||||
sa.register = true;
|
||||
}
|
||||
|
||||
if (to.meta?.title) document.title = to.meta.title as string;
|
||||
|
||||
if (to.query.digitalYiliToken) {
|
||||
utils.setSessionStorage('xToken', to.query.digitalYiliToken);
|
||||
}
|
||||
@@ -49,6 +69,14 @@ router.beforeEach((to, from, next) => {
|
||||
};
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(createPinia());
|
||||
app.use(router);
|
||||
// 神策数据插件
|
||||
app.use(sensorsData(), {
|
||||
// 测试环境
|
||||
server_url: 'https://digitaldmo.yili.com/sa?project=sensorstest'
|
||||
// 正式环境
|
||||
// server_url: 'https://digitaldmo.yili.com/sa?project=YIP'
|
||||
});
|
||||
app.mount('#app');
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router';
|
||||
import layout from '@/layouts/index.vue';
|
||||
import Design from '@/views/Design/Index.vue';
|
||||
import Redirect from '@/layouts/redirect.vue';
|
||||
import type { title } from 'process';
|
||||
import { getWXShareConfig } from '@/utils/share';
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
@@ -53,14 +53,26 @@ const router = createRouter({
|
||||
{
|
||||
path: '/create',
|
||||
name: 'create',
|
||||
meta: { title: '问卷编辑' },
|
||||
meta: {
|
||||
title: '问卷编辑',
|
||||
pureBGC: true,
|
||||
bgc: '#71b73c',
|
||||
header: {
|
||||
bgc: 'green',
|
||||
pureBGC: false
|
||||
}
|
||||
},
|
||||
component: () => import('../views/Survey/views/Create/Index.vue')
|
||||
},
|
||||
{
|
||||
path: '/templatePreview',
|
||||
name: 'templatePreview',
|
||||
meta: {
|
||||
title: '模板预览'
|
||||
title: '模板预览',
|
||||
header: {
|
||||
bgc: 'green',
|
||||
pureBGC: false
|
||||
}
|
||||
},
|
||||
component: () => import('@/views/Design/Preview.vue')
|
||||
},
|
||||
@@ -68,7 +80,11 @@ const router = createRouter({
|
||||
path: '/preview',
|
||||
name: 'preview',
|
||||
meta: {
|
||||
title: '预览'
|
||||
title: '预览',
|
||||
header: {
|
||||
bgc: 'green',
|
||||
pureBGC: false
|
||||
}
|
||||
},
|
||||
component: () => import('@/views/Survey/views/Preview/Index.vue')
|
||||
},
|
||||
@@ -92,14 +108,21 @@ const router = createRouter({
|
||||
{
|
||||
path: '/search',
|
||||
name: 'search',
|
||||
meta: {},
|
||||
meta: {
|
||||
|
||||
},
|
||||
component: () => import('@/views/HomeSearch/Index.vue')
|
||||
},
|
||||
{
|
||||
path: '/templateMarket',
|
||||
name: 'templateMarket',
|
||||
meta: {
|
||||
pureBGC: true
|
||||
title: '更多模板',
|
||||
pureBGC: true,
|
||||
header: {
|
||||
pureBGC: true
|
||||
},
|
||||
bgc: 'transparent'
|
||||
},
|
||||
component: () => import('@/views/Home/components/Market/Index.vue')
|
||||
},
|
||||
@@ -121,13 +144,7 @@ const router = createRouter({
|
||||
shareFunc: () => {
|
||||
if (window.ReactNativeWebView) {
|
||||
window.ReactNativeWebView.postMessage(
|
||||
JSON.stringify({
|
||||
type: 'shareModal',
|
||||
title: '分享标题',
|
||||
description: '分享副标题',
|
||||
thumbImageUrl: 'https://logo.png',
|
||||
webpageUrl: window.location.href
|
||||
})
|
||||
JSON.stringify(getWXShareConfig())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -141,6 +158,12 @@ const router = createRouter({
|
||||
name: 'design',
|
||||
meta: {},
|
||||
component: Design
|
||||
},
|
||||
{
|
||||
path: '/share/:code',
|
||||
name: 'share',
|
||||
meta: {},
|
||||
component: () => import('@/views/Share/Index.vue')
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
@@ -48,18 +48,18 @@ export const useQuestionStore = defineStore('questionStore', () => {
|
||||
|
||||
// 更新数据
|
||||
async function getQuestions() {
|
||||
const _url = new URL(location.href).pathname;
|
||||
const urlParamSearch = new URL(location.href).searchParams;
|
||||
let { data } = await AnswerApi.getQuetions({
|
||||
id: urlParamSearch.get('sn'),
|
||||
data: {
|
||||
is_preview: 1,
|
||||
is_template: 0,
|
||||
is_preview: _url.includes('templatePreview') ? 0 : 1,
|
||||
is_template: _url.includes('templatePreview') ? 1 : 0,
|
||||
// is_template: urlParamSearch.get('is_template') || 0,
|
||||
source: urlParamSearch.get('source') ?? ''
|
||||
}
|
||||
});
|
||||
data = data.data;
|
||||
console.log(`data:`, data);
|
||||
// 多语言
|
||||
data.languageType = [
|
||||
data?.survey?.style?.is_en_tips ? 'en' : '',
|
||||
|
||||
@@ -13,7 +13,7 @@ export default {
|
||||
select_random: 0,
|
||||
min_number: 1,
|
||||
max_number: 1,
|
||||
min_size: 0,
|
||||
min_size: 1,
|
||||
max_size: 1,
|
||||
is_file: 0,
|
||||
file_type: '0'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。
|
||||
import type { ComposeOption } from 'echarts/core';
|
||||
import * as echarts from 'echarts/core';
|
||||
import type { PieSeriesOption } from 'echarts/charts';
|
||||
import type { BarSeriesOption, PieSeriesOption } from 'echarts/charts';
|
||||
// 引入 饼状图
|
||||
import { PieChart } from 'echarts/charts';
|
||||
import { PieChart, BarChart} from 'echarts/charts';
|
||||
// 组件类型的定义后缀都为 ComponentOption
|
||||
import type {
|
||||
DatasetComponentOption,
|
||||
@@ -30,6 +30,7 @@ import { SVGRenderer } from 'echarts/renderers';
|
||||
// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
|
||||
type ECOption = ComposeOption<
|
||||
| PieSeriesOption
|
||||
| BarSeriesOption
|
||||
| TitleComponentOption
|
||||
| TooltipComponentOption
|
||||
| GridComponentOption
|
||||
@@ -48,6 +49,7 @@ echarts.use([
|
||||
UniversalTransition,
|
||||
SVGRenderer,
|
||||
PieChart,
|
||||
BarChart,
|
||||
LegendComponent
|
||||
]);
|
||||
|
||||
|
||||
25
src/utils/imgMap.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import png11 from '@/assets/img/market/11.png';
|
||||
import png13 from '@/assets/img/market/13.png';
|
||||
import png14 from '@/assets/img/home/14.png';
|
||||
import png15 from '@/assets/img/market/15.png';
|
||||
import png16 from '@/assets/img/market/16.png';
|
||||
import png17 from '@/assets/img/market/17.png';
|
||||
import png18 from '@/assets/img/market/18.png';
|
||||
import png31 from '@/assets/img/home/31.png';
|
||||
|
||||
const map = new Map([
|
||||
[11, png11],
|
||||
[13, png13],
|
||||
[14, png14],
|
||||
[15, png15],
|
||||
[16, png16],
|
||||
[17, png17],
|
||||
[18, png18],
|
||||
[31, png31]
|
||||
]);
|
||||
|
||||
const imgMap = (code: number) => {
|
||||
return map.get(code) || png11;
|
||||
};
|
||||
|
||||
export { imgMap };
|
||||
71
src/utils/plugins/sa/index.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { App } from 'vue';
|
||||
import sensors from './dist/v2/sensorsdata.es6';
|
||||
|
||||
/**
|
||||
* 神策数据 SDK
|
||||
*/
|
||||
export type Sensors = typeof sensors;
|
||||
|
||||
/**
|
||||
* 创建神策数据插件
|
||||
*/
|
||||
export function sensorsData() {
|
||||
return {
|
||||
install(app: App, options: any) {
|
||||
// 初始化神策 SDK
|
||||
sensors.init({
|
||||
show_log: true,
|
||||
is_track_single_page: false,
|
||||
use_client_time: true,
|
||||
send_type: 'beacon',
|
||||
heatmap: {
|
||||
clickmap: 'not_collect',
|
||||
scroll_notice_map: 'not_collect'
|
||||
},
|
||||
...(options || {})
|
||||
});
|
||||
|
||||
// 注册页面公共属性
|
||||
sensors.registerPage({
|
||||
product_name: '伊调研',
|
||||
platform_type: 'PC'
|
||||
});
|
||||
// 提供全局注入的 sensors 实例
|
||||
app.provide('sensors', sensors);
|
||||
// 注册到window中
|
||||
(globalThis as any).sa = sensors;
|
||||
|
||||
// 注册 saTrack 自定义指令
|
||||
registerDirective(app);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册 saTrack 指令,用于点击埋点
|
||||
* @param {App} app - Vue 应用实例
|
||||
*/
|
||||
function registerDirective(app: App) {
|
||||
function bindTrackListener(binding: any) {
|
||||
return () => {
|
||||
const { value: properties } = binding;
|
||||
|
||||
sensorsTrack(properties);
|
||||
};
|
||||
}
|
||||
|
||||
app.directive('sensorsTrack', {
|
||||
mounted(el, binding) {
|
||||
el.addEventListener('click', bindTrackListener(binding));
|
||||
},
|
||||
unmounted(el, binding) {
|
||||
// 清除绑定的事件
|
||||
el.removeEventListener('click', bindTrackListener(binding));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sensorsTrack(properties: any) {
|
||||
sensors.track('YiliResearch_PageClick', properties);
|
||||
}
|
||||
export { sensors, sensorsTrack };
|
||||
36
src/utils/share/index.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
type WXShareConfig = {
|
||||
type: string;
|
||||
title: string;
|
||||
description: string;
|
||||
thumbImageUrl: string;
|
||||
webpageUrl: string;
|
||||
};
|
||||
|
||||
let config: WXShareConfig = {
|
||||
type: 'shareModal',
|
||||
title: '分享标题',
|
||||
description: '',
|
||||
thumbImageUrl: 'https://logo.png',
|
||||
webpageUrl: window.location.href.replace('/ad/', '/share/')
|
||||
};
|
||||
|
||||
// 设置分享配置
|
||||
export const setWXShareConfig = (cb?: (config: WXShareConfig) => WXShareConfig) => {
|
||||
// 留作扩展
|
||||
cb && (config = cb(config));
|
||||
const url = new URL(window.location.href);
|
||||
// 获取 url 图片
|
||||
config.thumbImageUrl = url.origin + '/yl.png';
|
||||
// 获取 title 内容
|
||||
config.title = document.title || '伊调研';
|
||||
// 描述区域待定
|
||||
config.description = '';
|
||||
// 网页地址
|
||||
config.webpageUrl = window.location.href.replace('/ad/', '/share/');
|
||||
};
|
||||
|
||||
// 获取分享配置
|
||||
export const getWXShareConfig = () => {
|
||||
setWXShareConfig();
|
||||
return config;
|
||||
};
|
||||
3
src/utils/url/tools.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function isCollectUrl(urls: string[]) {
|
||||
return urls.some((url) => location.href.includes(url));
|
||||
}
|
||||
@@ -159,6 +159,27 @@ export function debounce(fn, wait) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 节流函数
|
||||
* @param fn {Function} 需要节流的函数
|
||||
* @param wait {number} 节流时间
|
||||
* @returns {Function} 节流后的函数
|
||||
*/
|
||||
export function throttle(fn, wait) {
|
||||
let lastTime = 0;
|
||||
return function() {
|
||||
const context = this;
|
||||
const args = arguments;
|
||||
const now = Date.now();
|
||||
if (now - lastTime >= wait) {
|
||||
console.log(`throttle`, now - lastTime);
|
||||
|
||||
fn.apply(context, args);
|
||||
lastTime = now;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
* @param type now:当前时间 endOfDay:当前时间的 23:59:59
|
||||
|
||||
@@ -1,19 +1,44 @@
|
||||
<script setup lang="ts">
|
||||
import { fetchBanners } from '@/hooks/request/banner';
|
||||
import { sensorsTrack } from '@/utils/plugins/sa';
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const router = useRouter();
|
||||
const { banners } = fetchBanners();
|
||||
const route = useRoute();
|
||||
const bannerInfo = computed(() => {
|
||||
console.log(banners.value);
|
||||
|
||||
return banners.value?.find((item: any) => item.code === route.params.code);
|
||||
});
|
||||
// 当前是否处于分享页面
|
||||
const hasShare = defineModel<boolean>('hasShare', { default: false });
|
||||
|
||||
function handleButtonClick() {
|
||||
saTrack(bannerInfo.value);
|
||||
if (!bannerInfo.value?.url) {
|
||||
router.push({ name: 'intelligentGeneration' });
|
||||
return;
|
||||
}
|
||||
window.location.href = bannerInfo.value?.url;
|
||||
}
|
||||
|
||||
function saTrack(record: any) {
|
||||
sensorsTrack({
|
||||
page_name: 'APP落地页',
|
||||
model_name: record?.code || '',
|
||||
button_name: '立即体验'
|
||||
});
|
||||
}
|
||||
/**
|
||||
* 在挂载之后重新修改 html title 内容
|
||||
*/
|
||||
setTimeout(() => {
|
||||
document.title = bannerInfo.value?.title || '伊调研';
|
||||
}, 800);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="banner-container">
|
||||
<section class="banner-container" :style="{ background: hasShare ? 'white' : undefined }">
|
||||
<section class="msg-info">
|
||||
<h3>{{ bannerInfo?.title }}</h3>
|
||||
<el-space spacer="|">
|
||||
@@ -24,13 +49,13 @@ const bannerInfo = computed(() => {
|
||||
<!-- banner内容 -->
|
||||
<article>
|
||||
<!-- 根据banner的类型使用不同的方式渲染 -->
|
||||
<section class="banner-text" v-if="bannerInfo?.type === 0">
|
||||
<section v-if="bannerInfo?.type === 0" class="banner-text">
|
||||
<p>{{ bannerInfo.synopsis }}</p>
|
||||
</section>
|
||||
<div class="banner-image" v-else-if="bannerInfo?.type === 1">
|
||||
<div v-else-if="bannerInfo?.type === 1" class="banner-image">
|
||||
<el-image fit="cover" loading="lazy" :src="bannerInfo.file_address" />
|
||||
</div>
|
||||
<section class="banner-video" v-else-if="bannerInfo?.type === 2">
|
||||
<section v-else-if="bannerInfo?.type === 2" class="banner-video">
|
||||
<video width="100%" height="auto" controls>
|
||||
<source :src="bannerInfo.file_address" type="video/mp4" />
|
||||
</video>
|
||||
@@ -38,20 +63,16 @@ const bannerInfo = computed(() => {
|
||||
</article>
|
||||
|
||||
<section
|
||||
v-if="bannerInfo?.is_display_button"
|
||||
style="
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 10px;
|
||||
"
|
||||
v-if="bannerInfo?.is_display_button && !hasShare"
|
||||
style="margin-top: 20px; width: 100%; position: sticky; bottom: 10px"
|
||||
>
|
||||
<!-- 立即进入 -->
|
||||
<el-button style="width: 95%; height: 50px; border-radius: 15px" color="#70b937">
|
||||
<el-text style="color: white">{{ bannerInfo.button_name }}</el-text>
|
||||
<el-button
|
||||
style="width: 100%; height: 50px; border-radius: 15px"
|
||||
color="#70b937"
|
||||
@click="handleButtonClick"
|
||||
>
|
||||
<el-text style="color: white">{{ bannerInfo?.button_name }}</el-text>
|
||||
</el-button>
|
||||
</section>
|
||||
</section>
|
||||
@@ -62,8 +83,6 @@ const bannerInfo = computed(() => {
|
||||
|
||||
.banner-container {
|
||||
padding: $gap * 2;
|
||||
margin-bottom: $gap * 6;
|
||||
|
||||
.msg-info {
|
||||
font-family: PingFangSC-Medium;
|
||||
h2 {
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<script setup lang="ts">
|
||||
import preview from '@/views/Survey/views/Preview/Index.vue';
|
||||
import { useTemplate } from '@/api/home';
|
||||
import { saveQuestions, snQuestions } from '@/api/design';
|
||||
import { getSurveyTemplate, saveQuestions, snQuestions } from '@/api/design';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useCounterStore } from '@/stores/counter';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@@ -29,12 +29,19 @@ const counterStore = useCounterStore();
|
||||
const store = storeToRefs(counterStore);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const pastTemplate = () => {
|
||||
|
||||
const pastTemplate = async () => {
|
||||
const { data } = await getSurveyTemplate(route.query.sn as string, {
|
||||
is_preview: 0,
|
||||
is_template: 1
|
||||
});
|
||||
const { title, introduction } = data.data.survey;
|
||||
|
||||
const query = {
|
||||
group_id: 0,
|
||||
source: 1,
|
||||
project_name: `${route.query.title}问卷 `,
|
||||
remarks: '为优化活动服务品质,烦请完成问卷,感谢配合',
|
||||
project_name: title,
|
||||
remarks: introduction,
|
||||
scene_code: route.query.parentCode,
|
||||
scene_code_info: route.query.code,
|
||||
// 很迷茫 模板新增 tag 空数组 非模板 就是k
|
||||
@@ -44,12 +51,12 @@ const pastTemplate = () => {
|
||||
if (res.data) {
|
||||
snQuestions({ sn: res.data.data.sn }).then((ques) => {
|
||||
if (ques.data) {
|
||||
ques.data.data.survey.introduction = `<p>为优化活动服务品质,烦请完成问卷,感谢配合!您的反馈至关重要!</p>`;
|
||||
ques.data.data.survey.introduction = introduction;
|
||||
store.questionsInfo.value = ques.data.data;
|
||||
saveQuestions({
|
||||
sn: res.data.data.sn,
|
||||
introduction: ques.data.data.survey.introduction,
|
||||
title: ques.data.data.survey.title
|
||||
introduction: introduction,
|
||||
title: title
|
||||
}).then((q) => {
|
||||
if (q.data) {
|
||||
router.replace({
|
||||
|
||||
@@ -53,12 +53,12 @@
|
||||
input-align="right"
|
||||
class="action-field"
|
||||
placeholder="0"
|
||||
:min="0"
|
||||
:min="1"
|
||||
:max="actionQuestion.config.max_size"
|
||||
@blur="handleMinSizeBlur"
|
||||
@update:model-value="
|
||||
(value) => {
|
||||
actionQuestion.config.min_size = value === '' ? 0 : value;
|
||||
actionQuestion.config.min_size = value === '' ? 1 : value;
|
||||
}
|
||||
"
|
||||
>
|
||||
@@ -78,7 +78,7 @@
|
||||
@blur="handleMaxSizeBlur"
|
||||
@update:model-value="
|
||||
(value) => {
|
||||
actionQuestion.config.max_size = value === '' ? 0 : value;
|
||||
actionQuestion.config.max_size = value === '' ? 1 : value;
|
||||
}
|
||||
"
|
||||
>
|
||||
|
||||
@@ -18,21 +18,46 @@
|
||||
></contenteditable>
|
||||
</template>
|
||||
<template #input>
|
||||
<textarea
|
||||
v-if="!element.config.line_height || element.config.line_height != 0"
|
||||
<el-input-number
|
||||
v-model="completionValue"
|
||||
v-if="element.config.text_type === 1"
|
||||
type="number"
|
||||
class="other_input"
|
||||
:placeholder="element.config.placeholder"
|
||||
:rows="element.config.line_height"
|
||||
></textarea>
|
||||
controls-position="right"
|
||||
:max="element.config.max"
|
||||
@blur="changeValue"
|
||||
style="padding: 0; background: #fff"
|
||||
></el-input-number>
|
||||
|
||||
<el-input-number
|
||||
v-model="completionValue"
|
||||
v-else-if="element.config.text_type === 2"
|
||||
:precision="element.config.decimal_few"
|
||||
:max="element.config.max"
|
||||
type="number"
|
||||
controls-position="right"
|
||||
class="other_input"
|
||||
@blur="changeValue"
|
||||
style="padding: 0; background: #fff"
|
||||
></el-input-number>
|
||||
<div v-else style="width: 92%">
|
||||
<textarea
|
||||
v-if="!element.config.line_height || element.config.line_height != 0"
|
||||
v-model="completionValue"
|
||||
class="other_input"
|
||||
:placeholder="element.config.placeholder"
|
||||
:rows="element.config.line_height"
|
||||
></textarea>
|
||||
</div>
|
||||
</template>
|
||||
</van-field>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { toRefs } from 'vue';
|
||||
|
||||
import { toRefs, watch } from 'vue';
|
||||
import { ElInputNumber } from 'element-plus';
|
||||
import { showFailToast } from 'vant';
|
||||
const completionValue = defineModel('completionValue', { default: '', type: String });
|
||||
const props = defineProps({
|
||||
isPreview: {
|
||||
@@ -58,12 +83,33 @@ const errorMessage = defineModel('errorMessage', {
|
||||
type: String,
|
||||
default: ''
|
||||
});
|
||||
|
||||
// 创建一个本地副本以保存更改
|
||||
const emit = defineEmits(['update:element']);
|
||||
const { element } = toRefs(props);
|
||||
const emitValue = () => {
|
||||
emit('update:element', element.value);
|
||||
};
|
||||
watch(
|
||||
() => completionValue.value,
|
||||
() => {
|
||||
if (!completionValue.value) {
|
||||
completionValue.value = null;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const changeValue = (values) => {
|
||||
console.log(element.value.config);
|
||||
if (
|
||||
completionValue.value < element.value.config.min &&
|
||||
element.value.config.min &&
|
||||
completionValue.value
|
||||
) {
|
||||
completionValue.value = null;
|
||||
showFailToast('请输入大于' + element.value.config.min + '的数字');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -34,7 +34,6 @@ const fileLimit = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
console.log(fileLimit.value);
|
||||
/**
|
||||
* 上传文件
|
||||
* @description 上传文件
|
||||
@@ -75,7 +74,7 @@ function handleFileUpload() {
|
||||
// 上传文件
|
||||
|
||||
// 生成答案
|
||||
answer.value = files;
|
||||
question.value.answer = files;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,40 @@
|
||||
<template>
|
||||
<input
|
||||
type="text"
|
||||
class="el-input"
|
||||
style="width: 100%"
|
||||
:name="`R${rowIndex + 1}`"
|
||||
:value="getInputValue(rowIndex, colIndex)"
|
||||
@change="handleMatrixTextChange(rowIndex, colIndex, $event)"
|
||||
/>
|
||||
<div>
|
||||
<!-- <el-input-number-->
|
||||
<!-- v-model="inputValueMTX"-->
|
||||
<!-- style="width: 100%"-->
|
||||
<!-- v-if="element.config.text_type === 1"-->
|
||||
<!-- controls-position="right"-->
|
||||
<!-- @change="(e) => ev"-->
|
||||
<!-- :max="element.config.max"-->
|
||||
<!-- @blur="changeValue(rowIndex, colIndex)"-->
|
||||
<!-- ></el-input-number>-->
|
||||
<!-- <el-input-number-->
|
||||
<!-- v-model="inputValueMTX"-->
|
||||
<!-- style="width: 100%"-->
|
||||
<!-- v-else-if="element.config.text_type === 2"-->
|
||||
<!-- controls-position="right"-->
|
||||
<!-- @change="(e) => handleMatrixTextChange(rowIndex, colIndex, { target: { value: e } })"-->
|
||||
<!-- :precision="element.config.decimal_few"-->
|
||||
<!-- :max="element.config.max"-->
|
||||
<!-- @blur="changeValue(rowIndex, colIndex)"-->
|
||||
<!-- ></el-input-number>-->
|
||||
<input
|
||||
type="text"
|
||||
class="el-input"
|
||||
style="width: 100%"
|
||||
:placeholder="element.config.placeholder"
|
||||
:name="`R${rowIndex + 1}`"
|
||||
:value="getInputValue(rowIndex, colIndex)"
|
||||
@change="handleMatrixTextChange(rowIndex, colIndex, $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ElInputNumber } from 'element-plus';
|
||||
import { ref } from 'vue';
|
||||
import { showFailToast } from 'vant';
|
||||
// 接受获取到的 col row 的索引参数
|
||||
const rowIndex = defineModel('rowIndex', { required: true, default: 0 });
|
||||
const colIndex = defineModel('colIndex', { required: true, default: 0 });
|
||||
@@ -21,8 +46,13 @@ const element = defineModel<question>('element', {
|
||||
/**/
|
||||
}
|
||||
});
|
||||
|
||||
const inputValueMTX = ref('');
|
||||
const emit = defineEmits(['update:matrixAnswer', 'update:rowRecord', 'update:element']);
|
||||
|
||||
function ev() {
|
||||
console.log(1231);
|
||||
}
|
||||
function getInputValue(row: number, col: number) {
|
||||
// console.log(`row: ${row}, col: ${col}`);
|
||||
// console.log(`rowRecord:`, rowRecord.value);
|
||||
@@ -35,6 +65,7 @@ function handleMatrixTextChange(row: number, col: number, e: Event) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const inputValue = target.value;
|
||||
|
||||
console.log(inputValue, 123);
|
||||
// 获取 colIndexArray
|
||||
if (!rowRecord.value[row]) {
|
||||
// 如果没有对应的row,创建一个
|
||||
@@ -46,6 +77,18 @@ function handleMatrixTextChange(row: number, col: number, e: Event) {
|
||||
// console.log(`rowRecord:`, rowRecord.value);
|
||||
}
|
||||
|
||||
const changeValue = (rowIndex, colIndex) => {
|
||||
if (
|
||||
inputValueMTX.value < element.value.config.min &&
|
||||
element.value.config.min &&
|
||||
inputValueMTX.value
|
||||
) {
|
||||
inputValueMTX.value = null;
|
||||
showFailToast('请输入大于' + element.value.config.min + '的数字');
|
||||
|
||||
handleMatrixTextChange(rowIndex, colIndex, { target: { value: '' } });
|
||||
}
|
||||
};
|
||||
const emitValue = () => {
|
||||
emit('update:element', element.value);
|
||||
};
|
||||
@@ -53,4 +96,15 @@ const emitValue = () => {
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '@/assets/css/theme';
|
||||
input[type='text'] {
|
||||
width: 100%;
|
||||
padding: 0 5px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
outline: 1px solid #f4f4f4;
|
||||
|
||||
&:focus {
|
||||
outline: 1px solid $theme-color;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
const answer = ref<FileList>();
|
||||
|
||||
// const answer = ref<FileList>();
|
||||
const answer = ref<FileList | []>([]);
|
||||
/**
|
||||
* 文件限制
|
||||
* @property {number} max - 最大文件大小
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
<script setup>
|
||||
import { fetchSurveys } from '@/hooks/request/useSurvey';
|
||||
import CreateSurvey from './components/CreateSurvey/Index.vue';
|
||||
import NewSurvey from './components/NewSurvey/index.vue';
|
||||
import { onMounted, 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';
|
||||
import ImageSlider from './components/ImageSlider/Index.vue';
|
||||
import SearchBar from '@/components/Search/Index.vue';
|
||||
import Navigation from '@/components/Navigation/Index.vue';
|
||||
import HomeRecommend from './components/HomeRecommend/Index.vue';
|
||||
import MineTask from '@/views/Home/components/MineTask/Index.vue';
|
||||
import router from '@/router';
|
||||
|
||||
const contentShow = ref(false);
|
||||
// 获取我的问卷数据
|
||||
const { surveys } = fetchSurveys();
|
||||
const keyword = ref('');
|
||||
onMounted(async () => {
|
||||
if (appBridge.isInReactNative()) {
|
||||
const appToken = utils.getSessionStorage('xToken');
|
||||
getUserInfo(appToken)
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
contentShow.value = true;
|
||||
const token = res.data.data.token;
|
||||
localStorage.setItem('plantToken', token);
|
||||
utils.setSessionStorage('userInfo', res.data.data);
|
||||
} else {
|
||||
contentShow.value = false;
|
||||
showFailToast(
|
||||
error.response.data?.message || error.data?.message || error.message || '服务器错误'
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
contentShow.value = false;
|
||||
showFailToast(error?.response?.data?.message || error?.message || '服务器错误');
|
||||
});
|
||||
} else {
|
||||
utils.setSessionStorage('xToken', 'f74ba36d7fc3468480648dedba5672ff');
|
||||
contentShow.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
function handleSearchClick() {
|
||||
router.push({ name: 'search' });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="contentShow" class="container-home">
|
||||
<div class="container-body">
|
||||
<!-- 搜索栏 -->
|
||||
<section class="search">
|
||||
<search-bar placeholder="请输入关键词" :value="keyword" @click="handleSearchClick" />
|
||||
</section>
|
||||
<!-- 首页轮播图 -->
|
||||
<section class="slider">
|
||||
<image-slider />
|
||||
</section>
|
||||
<create-survey :createdNewPage="false" />
|
||||
<!-- 最新问卷 -->
|
||||
<!--<last-survey/>-->
|
||||
<!-- 模板市场 -->
|
||||
<!-- <Market/> -->
|
||||
<!--底部新建问卷-->
|
||||
<NewSurvey />
|
||||
|
||||
<!-- 我的问卷 部分, 当问卷不存在时,显示推荐内容 -->
|
||||
<mine-task v-if="surveys?.length > 0" :surveys="surveys" />
|
||||
<home-recommend v-else class="home_recommend" />
|
||||
<navigation />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use '@/assets/css/theme';
|
||||
|
||||
.container-body {
|
||||
padding: 0 10px 80px;
|
||||
}
|
||||
|
||||
.search {
|
||||
margin: 0 -10px 0 -10px;
|
||||
padding: 10px;
|
||||
@extend %search-gradient;
|
||||
}
|
||||
|
||||
.slider {
|
||||
overflow: hidden;
|
||||
border-radius: theme.$card-radius;
|
||||
}
|
||||
|
||||
.home_recommend {
|
||||
margin: theme.$gap 0;
|
||||
}
|
||||
</style>
|
||||
<script setup>
|
||||
import { fetchSurveys } from '@/hooks/request/useSurvey';
|
||||
import CreateSurvey from './components/CreateSurvey/Index.vue';
|
||||
import NewSurvey from './components/NewSurvey/index.vue';
|
||||
import { onMounted, 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';
|
||||
import ImageSlider from './components/ImageSlider/Index.vue';
|
||||
import SearchBar from '@/components/Search/Index.vue';
|
||||
import Navigation from '@/components/Navigation/Index.vue';
|
||||
import HomeRecommend from './components/HomeRecommend/Index.vue';
|
||||
import MineTask from '@/views/Home/components/MineTask/Index.vue';
|
||||
import router from '@/router';
|
||||
|
||||
const contentShow = ref(false);
|
||||
// 获取我的问卷数据
|
||||
const { surveys } = fetchSurveys();
|
||||
const keyword = ref('');
|
||||
onMounted(async () => {
|
||||
if (appBridge.isInReactNative()) {
|
||||
const appToken = utils.getSessionStorage('xToken');
|
||||
getUserInfo(appToken)
|
||||
.then((res) => {
|
||||
if (res.data) {
|
||||
contentShow.value = true;
|
||||
const token = res.data.data.token;
|
||||
localStorage.setItem('plantToken', token);
|
||||
utils.setSessionStorage('userInfo', res.data.data);
|
||||
} else {
|
||||
contentShow.value = false;
|
||||
showFailToast(
|
||||
error.response.data?.message || error.data?.message || error.message || '服务器错误'
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
contentShow.value = false;
|
||||
showFailToast(error?.response?.data?.message || error?.message || '服务器错误');
|
||||
});
|
||||
} else {
|
||||
utils.setSessionStorage('xToken', 'f74ba36d7fc3468480648dedba5672ff');
|
||||
contentShow.value = true;
|
||||
}
|
||||
});
|
||||
|
||||
function handleSearchClick() {
|
||||
router.push({ name: 'search' });
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="contentShow" class="container-home">
|
||||
<div class="container-body">
|
||||
<!-- 搜索栏 -->
|
||||
<section class="search">
|
||||
<search-bar placeholder="请输入关键词" :value="keyword" @click="handleSearchClick" />
|
||||
</section>
|
||||
<!-- 首页轮播图 -->
|
||||
<section class="slider">
|
||||
<image-slider />
|
||||
</section>
|
||||
<create-survey :createdNewPage="false" />
|
||||
<!-- 最新问卷 -->
|
||||
<!--<last-survey/>-->
|
||||
<!-- 模板市场 -->
|
||||
<!-- <Market/> -->
|
||||
<!--底部新建问卷-->
|
||||
<NewSurvey />
|
||||
|
||||
<!-- 我的问卷 部分, 当问卷不存在时,显示推荐内容 -->
|
||||
<mine-task v-if="surveys?.length > 0" :surveys="surveys" />
|
||||
<home-recommend v-else class="home_recommend" />
|
||||
<navigation />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@use '@/assets/css/theme';
|
||||
|
||||
.container-body {
|
||||
padding: 0 10px 80px;
|
||||
}
|
||||
|
||||
.search {
|
||||
margin: 0 -10px 0 -10px;
|
||||
padding: 10px;
|
||||
@extend %search-gradient;
|
||||
}
|
||||
|
||||
.slider {
|
||||
overflow: hidden;
|
||||
border-radius: theme.$card-radius;
|
||||
}
|
||||
|
||||
.home_recommend {
|
||||
margin: theme.$gap 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -30,8 +30,10 @@ const createdQuestion = (item) => {
|
||||
const query = {
|
||||
group_id: 0,
|
||||
source: 1,
|
||||
project_name: `${item.title ? item.title : 'z'} `,
|
||||
remarks: item.title ? '为优化活动服务品质,烦请完成问卷,感谢配合!您的反馈至关重要!' : '请输入问卷描述',
|
||||
project_name: `${item.title ? item.title : '请输入问卷标题'} `,
|
||||
remarks: item.title
|
||||
? '为优化活动服务品质,烦请完成问卷,感谢配合!您的反馈至关重要!'
|
||||
: '请输入问卷描述',
|
||||
scene_code: item.parentCode,
|
||||
scene_code_info: item.code,
|
||||
tags: ''
|
||||
@@ -74,7 +76,7 @@ const createdApx = (res) => {
|
||||
snQuestions({ sn: res.data.data.sn }).then((ques) => {
|
||||
console.log(`res`, res);
|
||||
if (ques.data) {
|
||||
const {data} = res.data
|
||||
const { data } = res.data;
|
||||
ques.data.data.survey.introduction = `<p>${data.remarks}</p>`;
|
||||
store.questionsInfo.value = ques.data.data;
|
||||
saveQuestions({
|
||||
@@ -169,7 +171,7 @@ onMounted(() => {
|
||||
color: #000;
|
||||
|
||||
.create_survey_title {
|
||||
margin: 16px;
|
||||
margin: 16px 10px;
|
||||
font-size: 15px;
|
||||
font-family: PingFangSC-Heavy;
|
||||
font-weight: 900;
|
||||
|
||||
@@ -1,16 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import { escapeHTML } from '@/utils/stringTranslate';
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
const host = `https://yiligpt.x.digitalyili.com`;
|
||||
const path = '/aiagent/assistant/78907182-cc42-4072-abae-86ef67c1ecd3/share';
|
||||
const param = `?token=${localStorage.getItem('plantToken')}&source=app`;
|
||||
const host = window.location.origin;
|
||||
const path = import.meta.env.VITE_APP_AIAGENTURL + '?';
|
||||
const param = `token=${encodeURIComponent(localStorage.getItem('plantToken') as string)}&source=app`;
|
||||
const url = host + path + param;
|
||||
const iframe = ref<HTMLIFrameElement | null>(null);
|
||||
const router = useRouter();
|
||||
onMounted(() => {
|
||||
// 保存原始的window.open方法
|
||||
const originalOpen = window.open;
|
||||
|
||||
// 字符串转义
|
||||
// 监听iframe的load事件,确保iframe已完全加载
|
||||
iframe.value?.addEventListener('load', () => {
|
||||
try {
|
||||
const iframeWindow = iframe.value?.contentWindow;
|
||||
|
||||
// 尝试覆盖iframe的open方法
|
||||
if (iframeWindow) {
|
||||
// 方法一:直接覆盖
|
||||
try {
|
||||
iframeWindow.open = function (...args: any[]) {
|
||||
const url = new URL(args[0]); // 使用 URL API 解析
|
||||
const path = url.pathname; // 获取路径部分
|
||||
const query = Object.fromEntries(url.searchParams); // 将 search 转换为对象
|
||||
// 如果需要去除特定 host 前缀(如本地调试)
|
||||
if (url.host === window.location.host) {
|
||||
args[0] = args[0].replace(`https://${url.host}`, '');
|
||||
}
|
||||
router.push({
|
||||
path,
|
||||
query
|
||||
});
|
||||
};
|
||||
} catch (e) {
|
||||
// 方法二:如果直接覆盖失败,尝试使用defineProperty
|
||||
try {
|
||||
Object.defineProperty(iframeWindow, 'open', {
|
||||
value: function (...args: any[]) {
|
||||
// console.log('iframe中的open方法被调用,参数:', args);
|
||||
return originalOpen.apply(window, args as any);
|
||||
},
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
} catch (e2) {
|
||||
console.error('无法覆盖iframe的open方法(defineProperty):', e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('无法覆盖iframe的open方法:', error);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<iframe style="height: 100%; width: 100%" :src="escapeHTML(url)" frameborder="0" />
|
||||
<iframe ref="iframe" style="height: 100%; width: 100%" :src="url" frameborder="0"></iframe>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -1,93 +1,151 @@
|
||||
<script setup lang="ts">
|
||||
import { recommend } from '@/hooks/request/recommend';
|
||||
import { ref } from 'vue';
|
||||
import YlTable from '@/components/YlTable/Index.vue';
|
||||
import CommonLayout from '@/components/Layout/CommonLayout.vue';
|
||||
|
||||
// 外部获取的数据
|
||||
const { data } = recommend({});
|
||||
|
||||
const props = ref<TablePropsType[]>([
|
||||
{
|
||||
prop: 'rank',
|
||||
label: '排名',
|
||||
width: 58
|
||||
},
|
||||
{
|
||||
prop: 'trend_name',
|
||||
label: '趋势名称',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
prop: 'growth_ring_ratio',
|
||||
label: '声量增长环比',
|
||||
width: 120
|
||||
},
|
||||
{
|
||||
prop: 'sales_growth_ring_ratio',
|
||||
label: '销量增长环比',
|
||||
width: 120
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<van-cell class="home_recommend">
|
||||
<template #extra>
|
||||
<div style="width: 90vw">
|
||||
<common-layout title="123">
|
||||
<template #title>
|
||||
<h3 class="recommend-layout-title">内容推荐</h3>
|
||||
</template>
|
||||
|
||||
<span class="recommend-title">{{ data?.title || 'TOP5现制饮品风味' }}</span>
|
||||
<yl-table
|
||||
:header-style="{ background: '#E8F9F4' }"
|
||||
:data="data?.surveyTrendDataVOS"
|
||||
:props="props"
|
||||
/>
|
||||
<!-- 剧中展示提示语 -->
|
||||
<div style="width: 100%; margin-top: 10px; text-align: center">
|
||||
<span style="">- 最新数据及更多创新趋势请到YIP探索 - </span>
|
||||
</div>
|
||||
</common-layout>
|
||||
</div>
|
||||
</template>
|
||||
</van-cell>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '@/assets/css/theme' as *;
|
||||
|
||||
.home_recommend {
|
||||
border-radius: $card-radius;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.recommend-layout-title {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 800;
|
||||
font-size: 15px;
|
||||
color: #000000;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.recommend-title {
|
||||
color: #000000;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
line-height: 15px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { recommend } from '@/hooks/request/recommend';
|
||||
import { ref } from 'vue';
|
||||
import YlTable from '@/components/YlTable/Index.vue';
|
||||
import CommonLayout from '@/components/Layout/CommonLayout.vue';
|
||||
|
||||
// 外部获取的数据
|
||||
const { data } = recommend({});
|
||||
const props = ref<TablePropsType[]>([
|
||||
{
|
||||
prop: 'rank',
|
||||
label: '排名',
|
||||
width: 58,
|
||||
render: (h, p) => {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: {
|
||||
color: '#81B64C'
|
||||
}
|
||||
},
|
||||
p.row.rank
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'trend_name',
|
||||
label: '趋势名称',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
prop: 'growth_ring_ratio',
|
||||
label: '声量增长环比',
|
||||
width: 150,
|
||||
sortable: true,
|
||||
render: (h, p) => {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: {
|
||||
color: p.row.growth_ring_ratio > 0 ? '#DD6E4E' : '#81B64C'
|
||||
}
|
||||
},
|
||||
[
|
||||
h('span', p.row.growth_ring_ratio + '%'),
|
||||
h('i', {
|
||||
class: 'van-icon van-icon-play ml5',
|
||||
style: {
|
||||
transform: p.row.growth_ring_ratio > 0 ? 'rotate(-90deg)' : 'rotate(90deg)'
|
||||
}
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
prop: 'sales_growth_ring_ratio',
|
||||
label: '销量增长环比',
|
||||
width: 150,
|
||||
sortable: true,
|
||||
render: (h, p) => {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
style: {
|
||||
color: p.row.sales_growth_ring_ratio > 0 ? '#DD6E4E' : '#81B64C'
|
||||
}
|
||||
},
|
||||
|
||||
[
|
||||
h('span', p.row.sales_growth_ring_ratio + '%'),
|
||||
h('i', {
|
||||
class: 'van-icon van-icon-play ml5',
|
||||
style: {
|
||||
transform: p.row.sales_growth_ring_ratio > 0 ? 'rotate(-90deg)' : 'rotate(90deg)'
|
||||
}
|
||||
})
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<van-cell class="home_recommend">
|
||||
<template #extra>
|
||||
<div style="width: 90vw">
|
||||
<common-layout title="123">
|
||||
<template #title>
|
||||
<h3 class="recommend-layout-title">创新速递</h3>
|
||||
</template>
|
||||
|
||||
<span class="recommend-title">{{ data?.title }}</span>
|
||||
<yl-table
|
||||
style="margin-top: 10px"
|
||||
:header-style="{ background: '#E8F9F4' }"
|
||||
:data="data?.surveyTrendDataVOS"
|
||||
:props="props"
|
||||
/>
|
||||
<!-- 剧中展示提示语 -->
|
||||
<div class="more">
|
||||
<span>- 最新数据及更多创新趋势请到YIP探索 - </span>
|
||||
</div>
|
||||
</common-layout>
|
||||
</div>
|
||||
</template>
|
||||
</van-cell>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@use '@/assets/css/theme' as *;
|
||||
|
||||
.home_recommend {
|
||||
border-radius: $card-radius;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.recommend-layout-title {
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-weight: 800;
|
||||
font-size: 15px;
|
||||
color: #000000;
|
||||
line-height: 20px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.recommend-title {
|
||||
color: #000000;
|
||||
font-family:
|
||||
PingFangSC,
|
||||
PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
line-height: 15px;
|
||||
text-align: left;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.more {
|
||||
margin: 15px 5px 5px 5px;
|
||||
color: #919191;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useRouter } from 'vue-router';
|
||||
import { bannerInfo } from '@/views/AD/hooks/useAD';
|
||||
import { fetchBanners } from '@/hooks/request/banner';
|
||||
import { computed } from 'vue';
|
||||
import { sensorsTrack } from '@/utils/plugins/sa';
|
||||
|
||||
const { banners } = fetchBanners();
|
||||
|
||||
@@ -10,7 +11,7 @@ const router = useRouter();
|
||||
// const defineBanners = defineModel('banners');
|
||||
// 如果定义了 banner , 那么 banners 就不再初始化
|
||||
// defineBanners.value && updateBanners(defineBanners.value);
|
||||
const borderRadius = defineModel('borderRadius');
|
||||
const borderRadius = defineModel<number>('borderRadius');
|
||||
// 是否启用平铺展示
|
||||
const stack = defineModel<boolean>('stack', { default: true });
|
||||
// 上级传递的 banners
|
||||
@@ -21,9 +22,19 @@ const limit = defineModel<number>('limit');
|
||||
function handleBannerClick(banner: any) {
|
||||
// 把对应的信息给 AD 的 hooks
|
||||
bannerInfo.value = banner;
|
||||
|
||||
saTrack(banner);
|
||||
router.push({ name: 'ad', params: { code: banner.code } });
|
||||
}
|
||||
|
||||
function saTrack(record: any) {
|
||||
sensorsTrack({
|
||||
page_name: 'APP首页',
|
||||
model_name: record?.code || '',
|
||||
button_name: '轮播图'
|
||||
});
|
||||
}
|
||||
|
||||
const currentBanners = computed(() => {
|
||||
if (bannersList.value) {
|
||||
return stack.value ? bannersList.value : bannersList.value?.slice(0, limit.value);
|
||||
@@ -33,27 +44,29 @@ const currentBanners = computed(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<van-swipe :autoplay="5000" indicator-color="white" v-if="stack">
|
||||
<van-swipe-item v-for="banner in currentBanners" :key="banner.code">
|
||||
<div class="slider-container">
|
||||
<van-swipe v-if="stack" :autoplay="5000" indicator-color="white">
|
||||
<van-swipe-item v-for="banner in currentBanners" :key="banner.code">
|
||||
<el-image
|
||||
class="img"
|
||||
:style="{ borderRadius: borderRadius + 'px' }"
|
||||
:src="banner.banner_address"
|
||||
fit="contain"
|
||||
@click="handleBannerClick(banner)"
|
||||
/>
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
<div v-else>
|
||||
<el-image
|
||||
v-for="banner in currentBanners"
|
||||
:key="banner.code"
|
||||
class="img"
|
||||
:style="{ borderRadius: borderRadius + 'px' }"
|
||||
:src="banner.banner_address"
|
||||
fit="contain"
|
||||
@click="handleBannerClick(banner)"
|
||||
/>
|
||||
</van-swipe-item>
|
||||
</van-swipe>
|
||||
<div v-else>
|
||||
<el-image
|
||||
v-for="banner in currentBanners"
|
||||
:key="banner.code"
|
||||
class="img"
|
||||
:style="{ borderRadius: borderRadius + 'px' }"
|
||||
:src="banner.banner_address"
|
||||
fit="contain"
|
||||
@click="handleBannerClick(banner)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,62 +2,110 @@
|
||||
<section class="market-container">
|
||||
<!-- 模板 -->
|
||||
<div class="market">
|
||||
<search v-model:value="searchValue" @change="fetchTemplate"></search>
|
||||
<search v-model:value="searchValue" @search="fetchTemplate"></search>
|
||||
<van-tabs
|
||||
v-model:active="active"
|
||||
style="margin-top: 15px"
|
||||
class="px-1"
|
||||
@change="getMarketInfo"
|
||||
shrink
|
||||
duration="0"
|
||||
color="#6fb937"
|
||||
@change="handleChangeTab"
|
||||
>
|
||||
<section>
|
||||
<van-tab v-for="item in marketList" :key="item.title" :title="item.h5Title">
|
||||
<van-tab v-for="(item, index) in marketList" :key="item.title" :title="item.h5Title">
|
||||
<template #title>
|
||||
<section>{{ item.h5Title }}</section>
|
||||
<!-- <el-icon><el-img :src="item.h5Image" /></el-icon> -->
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 10px;
|
||||
"
|
||||
:style="{ background: active === index ? '#6fb937' : '' }"
|
||||
>
|
||||
<!-- 当标签不是处于激活状态的时候,取消图片展示 -->
|
||||
<img v-if="active === index" width="15px" height="15px" :src="imgMap(item.code)" />
|
||||
<section :style="{ color: active === index ? '#fff' : '#000000' }">
|
||||
{{ item.h5Title }}
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
</van-tab>
|
||||
</section>
|
||||
</van-tabs>
|
||||
</div>
|
||||
<market-item :info="marketInfo" :marketItem="marketItem" />
|
||||
<section v-infinite-scroll="loadData">
|
||||
<market-item :info="marketInfo" :marketItem="marketItem" />
|
||||
</section>
|
||||
<div class="more">
|
||||
<p>-更多模板期待您的探索-</p>
|
||||
<p v-if="marketInfo.length === 0 && searchValue">无符合要求结果</p>
|
||||
<p v-else>-更多模板期待您的探索-</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, watch } from 'vue';
|
||||
import MarketItem from '@/components/MarketItem/MarketItem.vue';
|
||||
import { getListScene, getSurveyTemplates } from '@/api/home';
|
||||
import Search from '@/components/Search/Index.vue';
|
||||
import { fetchSearchResult } from '@/hooks/request/useSearch';
|
||||
|
||||
const searchResult = ref([]);
|
||||
import { imgMap } from '@/utils/imgMap';
|
||||
import { throttle } from '@/utils/utils';
|
||||
import { functionsIn } from 'lodash';
|
||||
|
||||
const searchParm = {
|
||||
index: 1
|
||||
};
|
||||
const marketList = ref([]);
|
||||
const active = ref(null);
|
||||
const marketIndex = ref(0);
|
||||
const marketInfo = ref([]);
|
||||
const marketInfo = ref<any[]>([]);
|
||||
// 当前激活的 item 信息
|
||||
const marketItem = ref();
|
||||
// 当前的搜索指
|
||||
const searchValue = ref('');
|
||||
let keyword = '';
|
||||
|
||||
let loadDataSingle = false;
|
||||
/**
|
||||
* 如果这个搜索框没有值,默认清空 keyword, 然后重新加载列表
|
||||
*/
|
||||
watch(searchValue, (value) => {
|
||||
if (!value) {
|
||||
keyword = '';
|
||||
fetchTemplate();
|
||||
}
|
||||
});
|
||||
|
||||
const getTableList = async () => {
|
||||
const res = await getListScene();
|
||||
// 将 tabs 数据存放到 sessionStorage 中
|
||||
sessionStorage.setItem('marketList', JSON.stringify(res.data.data));
|
||||
|
||||
if (res.data.code === 0) {
|
||||
res.data.data.forEach((item) => {
|
||||
// if (item.parentCode && item.parentCode === 1) {
|
||||
marketList.value.push(item);
|
||||
// }
|
||||
});
|
||||
getMarketInfo(marketList.value[0]);
|
||||
marketList.value = res.data.data;
|
||||
}
|
||||
};
|
||||
|
||||
watch(active, () => {
|
||||
console.log(`active change`, active.value);
|
||||
});
|
||||
|
||||
// 开始请求所有 table 的数据
|
||||
getTableList();
|
||||
|
||||
const getMarketInfo = async (item: string | number, title?: string) => {
|
||||
marketIndex.value = item as number;
|
||||
const data = marketList.value.filter((market, index) => item === index)[0];
|
||||
|
||||
const data = marketList.value[active.value];
|
||||
|
||||
if (data) {
|
||||
const params = {
|
||||
page: 1,
|
||||
page: searchParm.index,
|
||||
// 此字段无法脱离组件使用
|
||||
keyword,
|
||||
per_page: 10,
|
||||
group_id: 0,
|
||||
is_public: 1,
|
||||
@@ -67,31 +115,60 @@ const getMarketInfo = async (item: string | number, title?: string) => {
|
||||
|
||||
// 获取相应的 Info 信息
|
||||
const res = await getSurveyTemplates(params);
|
||||
console.log(`template market res`, res);
|
||||
const meta = res.data.meta;
|
||||
const { current_page, last_page } = meta;
|
||||
|
||||
if (res.data.code === 0) {
|
||||
marketInfo.value = res.data.data;
|
||||
// marketInfo.value = res.data.data;
|
||||
marketInfo.value = [...marketInfo.value, ...res.data.data];
|
||||
}
|
||||
// 获取对应的 item 信息
|
||||
marketItem.value = marketList.value.find((item) => item.h5Title === title);
|
||||
|
||||
// 后置处理. 解构出当前和最后一页内容,然后判断是否相等,如果相等,则取消数据加载
|
||||
if (current_page === last_page) {
|
||||
loadDataSingle = false;
|
||||
} else {
|
||||
searchParm.index++;
|
||||
loadDataSingle = true;
|
||||
}
|
||||
// 获取对应的 itme 信息
|
||||
marketItem.value = marketList.value.find((item) => item.title === title);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 当 table 切换的时候, 重置当前的参数数据
|
||||
*/
|
||||
function handleChangeTab() {
|
||||
searchParm.index = 1;
|
||||
marketInfo.value = [];
|
||||
searchData();
|
||||
}
|
||||
/**
|
||||
* 搜索模板
|
||||
* @param keyword 搜索关键词
|
||||
*/
|
||||
function fetchTemplate() {
|
||||
// const { templates } = fetchSearchResult(searchValue.value);
|
||||
// searchResult.value = templates;
|
||||
// 当点击搜索的时候, 重置当前的参数数据
|
||||
keyword = searchValue.value;
|
||||
handleChangeTab();
|
||||
}
|
||||
/**
|
||||
* 这个函数负责调用搜索
|
||||
*/
|
||||
function searchData() {
|
||||
getMarketInfo(marketIndex.value);
|
||||
}
|
||||
const debounceFn = throttle(searchData, 500);
|
||||
function loadData() {
|
||||
if (!loadDataSingle) return;
|
||||
debounceFn();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getTableList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.van-tabs__line) {
|
||||
background-color: #70b937;
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:deep(.van-tab--active) {
|
||||
@@ -107,7 +184,7 @@ onMounted(() => {
|
||||
.market {
|
||||
padding: 20px;
|
||||
border-radius: 0 0 16px 16px;
|
||||
background-color: red;
|
||||
background-color: #fff;
|
||||
|
||||
.market_title {
|
||||
margin-bottom: 5px;
|
||||
|
||||
@@ -1,19 +1,66 @@
|
||||
<script setup lang="ts">
|
||||
import QuestionList from './components/QuestionList.vue';
|
||||
import { ref } from 'vue';
|
||||
import { ref, watch, nextTick } from 'vue';
|
||||
import { isDrag } from './hooks/useDragEvent';
|
||||
const active = ref(0);
|
||||
const total = ref(0);
|
||||
const surveys = defineModel('surveys', { required: true });
|
||||
function setActive(act, tol) {
|
||||
function setActive(act: number, tol: number) {
|
||||
active.value = act;
|
||||
total.value = tol;
|
||||
}
|
||||
const swiper = ref();
|
||||
const questionComat = ref([]);
|
||||
function handleDragStart() {
|
||||
isDrag.value = true;
|
||||
}
|
||||
watch(
|
||||
() => active.value,
|
||||
(value) => {
|
||||
updateSwiperHeight();
|
||||
}
|
||||
);
|
||||
|
||||
nextTick(() => {
|
||||
if (questionComat.value && questionComat.value.length > 0) {
|
||||
console.log('questionComat 已经加载完成');
|
||||
updateSwiperHeight();
|
||||
}
|
||||
});
|
||||
|
||||
function updateSwiperHeight() {
|
||||
setTimeout(() => {
|
||||
if (swiper.value && questionComat.value[active.value]) {
|
||||
console.log(questionComat.value[active.value]?.$el.scrollHeight, '123');
|
||||
if (questionComat.value[active.value]?.$el.scrollHeight === 0) {
|
||||
updateSwiperHeight();
|
||||
return false;
|
||||
}
|
||||
|
||||
swiper.value.$el.style.height =
|
||||
questionComat.value[active.value]?.$el.scrollHeight + 30 + 'px';
|
||||
swiper.value.resize();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
function slideChange() {
|
||||
updateSwiperHeight();
|
||||
}
|
||||
function handleDragEnd() {
|
||||
isDrag.value = false;
|
||||
// setTimeout(() => {
|
||||
// // 获取高度
|
||||
// swiper.value.$el.style.height = questionComat.value[active.value].$el.scrollHeight + 30 + 'px';
|
||||
// swiper.value.resize();
|
||||
// }, 500);
|
||||
// swiper.value.height = questionList.value.;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="carousel-container">
|
||||
<div class="title">
|
||||
<span>我的任务</span>
|
||||
<span style="margin-top: 6px">我的任务</span>
|
||||
<div class="carousel-indicators">
|
||||
<i
|
||||
v-for="(item, index) in total"
|
||||
@@ -24,9 +71,15 @@ function setActive(act, tol) {
|
||||
<!--分页器。如果放置在swiper外面,需要自定义样式。-->
|
||||
</div>
|
||||
<div>
|
||||
<van-swipe :loop="false">
|
||||
<van-swipe :loop="false" @drag-start="handleDragStart" @drag-end="handleDragEnd" ref="swiper">
|
||||
<van-swipe-item v-for="question in surveys" :key="question?.sn">
|
||||
<question-list :survey="question" style="max-width: 100vw; overflow: hidden" />
|
||||
<question-list
|
||||
@slideChange="slideChange"
|
||||
:parentRef="swiper"
|
||||
:survey="question"
|
||||
style="max-width: 100vw; overflow: hidden"
|
||||
ref="questionComat"
|
||||
/>
|
||||
</van-swipe-item>
|
||||
<template #indicator="{ active, total }">
|
||||
{{ setActive(active, total) }}
|
||||
|
||||
@@ -7,13 +7,23 @@ import { fetchSingleSurvey } from '@/hooks/request/useSurvey';
|
||||
import YlSwiper from '@/components/YlSwiper/Index.vue';
|
||||
import EmptyContainer from '@/views/Survey/components/EmptyContainer.vue';
|
||||
import emptyImg from '@/assets/img/emptyImg.png';
|
||||
import { isDrag } from '../hooks/useDragEvent';
|
||||
import { defineEmits } from 'vue';
|
||||
|
||||
const survey = defineModel<SurveyItem>('survey');
|
||||
const parentRef = defineModel<any>('parentRef');
|
||||
// 获取问卷分析数据
|
||||
const { questionAnalysis } = useFetchAnalysis(survey.value?.sn as string);
|
||||
const { currentSurvey } = fetchSingleSurvey(survey.value?.sn as string);
|
||||
|
||||
const disableInsight = ref(true);
|
||||
|
||||
// 定义事件
|
||||
const emit = defineEmits(['slideChange']);
|
||||
const slideChange = function (swiper: any) {
|
||||
parentRef.value.resize();
|
||||
emit('slideChange', { swiper, activeIndex: swiper.activeIndex });
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -32,16 +42,18 @@ const disableInsight = ref(true);
|
||||
<section class="analysis-info">
|
||||
<!-- 方式一:使用默认插槽,手动添加 swiper-slide 元素 -->
|
||||
<yl-swiper
|
||||
@slideChange="slideChange"
|
||||
:pagination="!isDrag"
|
||||
:slides-per-view="1"
|
||||
:centered-slides="true"
|
||||
:pagination="true"
|
||||
:navigation="true"
|
||||
:navigation="!isDrag"
|
||||
:loop="false"
|
||||
:autoHeight="true"
|
||||
:space-between="0"
|
||||
:allow-touch-move="false"
|
||||
>
|
||||
<swiper-slide v-for="analysis in questionAnalysis" :key="analysis.stem">
|
||||
<analysis-info :sn="survey?.sn" :questionAnalysis="[analysis]" />
|
||||
<swiper-slide v-for="(analysis, index) in questionAnalysis" :key="analysis.stem">
|
||||
<analysis-info :sn="survey?.sn" :questionAnalysis="[analysis]" :parentIndex="index + 1" />
|
||||
</swiper-slide>
|
||||
<div class="empty-container" v-if="questionAnalysis?.length === 0">
|
||||
<empty-container :error-msg="'本问卷暂无有效答题数据'" :img-src="emptyImg" />
|
||||
|
||||
11
src/views/Home/components/MineTask/hooks/useDragEvent.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { watch,ref } from "vue";
|
||||
|
||||
/**
|
||||
* 这个是用于判断是否正在拖动,来决定是否显示左右轮播图的按钮
|
||||
*/
|
||||
const isDrag = ref(false);
|
||||
|
||||
watch(isDrag, (val) => {
|
||||
// console.log('isDrag', val);
|
||||
})
|
||||
export {isDrag}
|
||||
@@ -4,12 +4,13 @@ import MarketItem from '@/components/TemplateMarketItem/Index.vue';
|
||||
import { templates } from '../../Hooks/useSurveySearch';
|
||||
import { consoleSurveys, useTemplate } from '@/api/home';
|
||||
import { saveQuestions, snQuestions } from '@/api/design';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useCounterStore } from '@/stores/counter';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
|
||||
// 获取 Store 实例
|
||||
const counterStore = useCounterStore();
|
||||
@@ -18,6 +19,27 @@ const store = storeToRefs(counterStore as any);
|
||||
const limit = defineModel<number>('limit');
|
||||
const currentTemplate = computed(() => templates.value.slice(0, limit.value));
|
||||
function handleTemplateClick(template: any) {
|
||||
// ?sn=4O5xanLV&is_template=1&source=4O5xanLV&title=报名签到&parentCode=1&scene_code_info=11&user=苗闻博"e_nums=3
|
||||
|
||||
const isSearch = route.path.includes('/search');
|
||||
|
||||
router.push({
|
||||
path: '/templatePreview',
|
||||
query: {
|
||||
sn: template.sn,
|
||||
user: template.creater_user,
|
||||
is_template: 1,
|
||||
source: template.sn,
|
||||
// H5TITLE 是为搜索页面传入的参数做兼容
|
||||
title: isSearch ? template.h5_title : template.title,
|
||||
parentCode: template.parentCode,
|
||||
scene_code_info: template.scene_code_info,
|
||||
quote_nums: template.quote_nums
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
const query = {
|
||||
group_id: 0,
|
||||
source: 1,
|
||||
|
||||
11
src/views/Share/Index.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
|
||||
const shareComponent = defineAsyncComponent(() => import('@/views/AD/Index.vue'));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<share-component :has-share="true" />
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,42 +1,40 @@
|
||||
<template>
|
||||
<div class="survey-search">
|
||||
<nav-search
|
||||
v-model:value="searchValue"
|
||||
@search="handleSearchClick"
|
||||
@cancel="handleCancelClick"
|
||||
/>
|
||||
<!-- <nav-search
|
||||
placeholder="请输入关键词"
|
||||
v-model:value="searchValue"
|
||||
@click="() => $router.push({ name: 'search' })"
|
||||
/> -->
|
||||
</div>
|
||||
<div v-loading="requestLoading" class="new-survey-container">
|
||||
<div style="margin-bottom: 80px">
|
||||
<van-list v-model:loading="loading" :finished="finished" @load="handleLoadSurveys">
|
||||
<template #finished>
|
||||
<!-- 如果存在搜索文字的话,显示没有更多了 -->
|
||||
<span v-if="searchValue">
|
||||
<el-text>无符合要求的结果</el-text>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-if="survey.length > 0">
|
||||
<div v-for="item in survey" :key="item" class="new-survey_item">
|
||||
<survey-item :survey="item" :is-analysis="true" :disable-action-button="false" />
|
||||
</div>
|
||||
</template>
|
||||
<!-- 如果问卷等于0的话,显示空容器 -->
|
||||
<empty-container
|
||||
v-if="survey.length === 0 && !searchValue"
|
||||
:img-src="emptyImg"
|
||||
:show-button="true"
|
||||
@handle-click="handleEmptyClick"
|
||||
/>
|
||||
<NewSurvey v-model:show="showModel" />
|
||||
</van-list>
|
||||
<section>
|
||||
<!-- survey container -->
|
||||
<div class="survey-search">
|
||||
<nav-search
|
||||
v-model:value="searchValue"
|
||||
@search="handleSearchClick"
|
||||
@cancel="handleCancelClick"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-loading="requestLoading" class="new-survey-container">
|
||||
<div style="margin-bottom: 80px">
|
||||
<van-list v-model:loading="loading" :finished="finished" @load="handleLoadSurveys">
|
||||
<template #finished>
|
||||
<!-- 如果存在搜索文字的话,显示没有更多了 -->
|
||||
<span v-if="searchValue">
|
||||
<el-text>无符合要求的结果</el-text>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<template v-if="survey.length > 0">
|
||||
<div v-for="item in survey" :key="item" class="new-survey_item">
|
||||
<survey-item :survey="item" :is-analysis="true" :disable-action-button="false" />
|
||||
</div>
|
||||
</template>
|
||||
<!-- 如果问卷等于0的话,显示空容器 -->
|
||||
<empty-container
|
||||
v-if="survey.length === 0 && !searchValue"
|
||||
:img-src="emptyImg"
|
||||
:show-button="true"
|
||||
@handle-click="handleEmptyClick"
|
||||
/>
|
||||
<NewSurvey v-model:show="showModel" />
|
||||
</van-list>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -137,8 +135,8 @@ function handleEmptyClick() {
|
||||
}
|
||||
|
||||
.new-survey-container {
|
||||
margin: 0px 3px;
|
||||
|
||||
// margin: 0px 3px;
|
||||
overflow: hidden;
|
||||
.new-survey_item {
|
||||
margin: 0 10px 10px;
|
||||
padding: 10px;
|
||||
|
||||
@@ -1,40 +1,43 @@
|
||||
<script setup lang="ts">
|
||||
import { useCssModule } from 'vue';
|
||||
|
||||
const errorMsg = defineModel<string>('errorMsg', { default: ' - 更多任务期待您的创建 - ' });
|
||||
const showButton = defineModel<boolean>('showButton', { default: false });
|
||||
const imgSrc = defineModel<string>('imgSrc');
|
||||
|
||||
const emit = defineEmits(['handle-click']);
|
||||
const style = useCssModule();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-empty>
|
||||
<template #image v-if="imgSrc">
|
||||
<slot>
|
||||
<!-- 如果放了图片,默认展示图片位置 -->
|
||||
<img :src="imgSrc" alt="" :class="style.img" />
|
||||
</slot>
|
||||
</template>
|
||||
<template #description>
|
||||
<el-text>{{ errorMsg }}</el-text>
|
||||
</template>
|
||||
<el-button color="#71b73c" v-if="showButton" @click="emit('handle-click')" class="btn">
|
||||
<div style="color: #fff">+ 新建问卷</div>
|
||||
</el-button>
|
||||
</el-empty>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss" module>
|
||||
.img {
|
||||
width: 30vw;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: #fff;
|
||||
font-weight: 400;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { useCssModule } from 'vue';
|
||||
import emptyImg from '@/assets/img/emptyImg.png';
|
||||
|
||||
const errorMsg = defineModel<string>('errorMsg', { default: ' - 更多任务期待您的创建 - ' });
|
||||
const showButton = defineModel<boolean>('showButton', { default: false });
|
||||
const imgSrc = defineModel<string>('imgSrc', { default: emptyImg });
|
||||
|
||||
const emit = defineEmits(['handle-click']);
|
||||
const style = useCssModule();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-empty>
|
||||
<template #image v-if="imgSrc">
|
||||
<slot>
|
||||
<!-- 如果放了图片,默认展示图片位置 -->
|
||||
<img :src="imgSrc" alt="" :class="style.img" />
|
||||
</slot>
|
||||
</template>
|
||||
<template #description>
|
||||
<slot name="description">
|
||||
<el-text>{{ errorMsg }}</el-text>
|
||||
</slot>
|
||||
</template>
|
||||
<el-button color="#71b73c" v-if="showButton" @click="emit('handle-click')" class="btn">
|
||||
<div style="color: #fff">+ 新建问卷</div>
|
||||
</el-button>
|
||||
</el-empty>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss" module>
|
||||
.img {
|
||||
width: 30vw;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color: #fff;
|
||||
font-weight: 400;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,8 +19,9 @@ import {
|
||||
clearSurveys
|
||||
} from '@/views/Survey/hooks/useSurveyData';
|
||||
import ai from '@/assets/img/analysis/ai.svg';
|
||||
import { ref } from 'vue';
|
||||
import { computed, onMounted, ref, useTemplateRef, type CSSProperties } from 'vue';
|
||||
import { formatTime } from '@/utils/date';
|
||||
import { windowWidth } from 'vant/lib/utils';
|
||||
|
||||
const form = ref({
|
||||
page: 0,
|
||||
@@ -28,6 +29,8 @@ const form = ref({
|
||||
project_name: ''
|
||||
});
|
||||
|
||||
const titleRef = useTemplateRef('titleRef');
|
||||
|
||||
// router
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
@@ -54,6 +57,34 @@ function setImg(code: number) {
|
||||
return imageMap[code] || png11;
|
||||
}
|
||||
|
||||
// 是否显示 title 的 popover
|
||||
const showTitlePop = ref(false);
|
||||
// 是否是短标题 title; 在组件挂载之后会根据 title 的宽度来判断
|
||||
const isShortTitle = ref(false);
|
||||
onMounted(() => {
|
||||
// console.log(titleRef.value);
|
||||
if (titleRef.value) {
|
||||
const offsetWidth = titleRef.value.$el.offsetWidth;
|
||||
isShortTitle.value = 120 <= offsetWidth;
|
||||
}
|
||||
});
|
||||
|
||||
const surveyTitleStyle = computed<CSSProperties>(() => {
|
||||
const isPublishNumber = survey.value.is_publish_number;
|
||||
const isSurveyTime = survey.value.is_time;
|
||||
let width = '';
|
||||
if (isSurveyTime && isPublishNumber) {
|
||||
if (isShortTitle.value) {
|
||||
width = '25vw';
|
||||
}
|
||||
} else if (isSurveyTime) {
|
||||
width = '170px';
|
||||
}
|
||||
return {
|
||||
maxWidth: width
|
||||
};
|
||||
});
|
||||
|
||||
function editItem(item: SurveyItem) {
|
||||
router.push({
|
||||
path: '/create',
|
||||
@@ -69,8 +100,8 @@ function toPreview(item: SurveyItem) {
|
||||
query: {
|
||||
sn: item.sn,
|
||||
name: item.project_name,
|
||||
source: 0,
|
||||
is_template: 1
|
||||
source: 0
|
||||
// is_template: 1
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -149,7 +180,7 @@ function copyItem(item: SurveyItem) {
|
||||
<div style="position: relative">
|
||||
<div style="display: flex; justify-content: space-between; margin: 10px 0">
|
||||
<div class="survey_item_info_title">
|
||||
<el-text style="max-width: 100px">
|
||||
<el-text ref="titleRef" :style="surveyTitleStyle">
|
||||
<b v-html="survey.project_name"></b>
|
||||
</el-text>
|
||||
<el-text v-if="survey.is_publish_number" class="wrap">
|
||||
@@ -281,6 +312,11 @@ function copyItem(item: SurveyItem) {
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
right: -25px;
|
||||
|
||||
img {
|
||||
transform: translate(0%, -7%);
|
||||
width: 98px;
|
||||
}
|
||||
}
|
||||
|
||||
.survey_item_info_title {
|
||||
|
||||
@@ -1,105 +1,140 @@
|
||||
import { getSurveysPage, deleteSurveys, saveTemplates } from '@/api/home';
|
||||
import { ref } from 'vue';
|
||||
import { showDialog, showConfirmDialog, showFailToast, showToast } from 'vant';
|
||||
import { getSurveysDetail } from '@/api/design';
|
||||
|
||||
const searchValue = ref('');
|
||||
const survey = ref<SurveyItem[]>([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const requestLoading = ref(false);
|
||||
const finished = ref(false);
|
||||
const currentSurvey = ref<SurveyItem>();
|
||||
|
||||
const requestSingle = ref(true);
|
||||
|
||||
async function fetchSingleSurvey(sn: string) {
|
||||
const res = await getSurveysDetail(sn);
|
||||
// const res = await getSetting({sn})
|
||||
// console.log(res);
|
||||
if (res.data.code === 0) {
|
||||
currentSurvey.value = res.data.data;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchSurveys(form: any) {
|
||||
if (!requestSingle.value) return;
|
||||
requestSingle.value = false;
|
||||
requestLoading.value = true;
|
||||
const params = {
|
||||
page: form.page,
|
||||
per_page: form.pageSize,
|
||||
group_id: 0,
|
||||
// project_name: searchValue.value
|
||||
key_word: form.key_word
|
||||
};
|
||||
const res = await getSurveysPage(params);
|
||||
if (res.data.code === 0) {
|
||||
survey.value = survey.value.concat(res.data.data);
|
||||
total.value = res.data.meta.total;
|
||||
loading.value = false;
|
||||
// 数据全部加载完成
|
||||
if (survey.value.length >= total.value) {
|
||||
finished.value = true;
|
||||
}
|
||||
} else {
|
||||
// Toast()
|
||||
}
|
||||
requestLoading.value = false;
|
||||
requestSingle.value = true;
|
||||
}
|
||||
|
||||
function deleteItem(item: SurveyItem, form: any) {
|
||||
showDialog({
|
||||
title: `确认删除问卷 "${item.project_name}" ?`,
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#03B03C'
|
||||
})
|
||||
.then(async() => {
|
||||
const res = await deleteSurveys(item.sn);
|
||||
if (res.data.message) {
|
||||
showToast(res.data.message);
|
||||
} else {
|
||||
showToast('删除成功!');
|
||||
}
|
||||
form.page = 1;
|
||||
clearSurveys();
|
||||
await fetchSurveys(form);
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
}
|
||||
|
||||
// 保存为模板
|
||||
async function saveTemplate(item: SurveyItem) {
|
||||
const data = JSON.parse(JSON.stringify(item));
|
||||
const res = await saveTemplates(item.sn, data);
|
||||
if (res.data.code === 200 || res.data.code === 201) {
|
||||
showConfirmDialog({
|
||||
message: '模板保存成功,请前往更多模板页面查看',
|
||||
showCancelButton: false
|
||||
});
|
||||
} else {
|
||||
showFailToast(res.data);
|
||||
}
|
||||
}
|
||||
|
||||
function clearSurveys() {
|
||||
survey.value = [];
|
||||
}
|
||||
|
||||
export {
|
||||
fetchSurveys,
|
||||
loading,
|
||||
finished,
|
||||
survey,
|
||||
total,
|
||||
searchValue,
|
||||
deleteItem,
|
||||
saveTemplate,
|
||||
currentSurvey,
|
||||
requestLoading,
|
||||
fetchSingleSurvey,
|
||||
clearSurveys
|
||||
};
|
||||
import { getSurveysPage, deleteSurveys, saveTemplates } from '@/api/home';
|
||||
import { ref } from 'vue';
|
||||
import { showDialog, showConfirmDialog, showFailToast, showToast } from 'vant';
|
||||
import { getSurveysDetail } from '@/api/design';
|
||||
import { getQuestionList } from '@/api/survey';
|
||||
import { questionTypeMap } from '@/utils/question/typeMapping';
|
||||
|
||||
const searchValue = ref('');
|
||||
const survey = ref<SurveyItem[]>([]);
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const requestLoading = ref(false);
|
||||
const finished = ref(false);
|
||||
const currentSurvey = ref<SurveyItem>();
|
||||
|
||||
const requestSingle = ref(true);
|
||||
|
||||
async function fetchSingleSurvey(sn: string) {
|
||||
const res = await getSurveysDetail(sn);
|
||||
// const res = await getSetting({sn})
|
||||
// console.log(res);
|
||||
if (res.data.code === 0) {
|
||||
currentSurvey.value = res.data.data;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchSurveys(form: any) {
|
||||
if (!requestSingle.value) return;
|
||||
requestSingle.value = false;
|
||||
requestLoading.value = true;
|
||||
const params = {
|
||||
page: form.page,
|
||||
per_page: form.pageSize,
|
||||
group_id: 0,
|
||||
// project_name: searchValue.value
|
||||
key_word: form.key_word
|
||||
};
|
||||
const res = await getSurveysPage(params);
|
||||
if (res.data.code === 0) {
|
||||
survey.value = survey.value.concat(res.data.data);
|
||||
total.value = res.data.meta.total;
|
||||
loading.value = false;
|
||||
// 数据全部加载完成
|
||||
if (survey.value.length >= total.value) {
|
||||
finished.value = true;
|
||||
}
|
||||
} else {
|
||||
// Toast()
|
||||
}
|
||||
requestLoading.value = false;
|
||||
requestSingle.value = true;
|
||||
}
|
||||
|
||||
function deleteItem(item: SurveyItem, form: any) {
|
||||
showDialog({
|
||||
title: `确认删除问卷 "${item.project_name}" ?`,
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: '#03B03C'
|
||||
})
|
||||
.then(async () => {
|
||||
const res = await deleteSurveys(item.sn);
|
||||
if (res.data.message) {
|
||||
showToast(res.data.message);
|
||||
} else {
|
||||
showToast('删除成功!');
|
||||
}
|
||||
form.page = 1;
|
||||
clearSurveys();
|
||||
await fetchSurveys(form);
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
}
|
||||
|
||||
// 保存为模板
|
||||
async function saveTemplate(item: SurveyItem) {
|
||||
const data = JSON.parse(JSON.stringify(item));
|
||||
|
||||
// 如果没有通过校验, 弹出提示窗不进行下一步
|
||||
if (!(await validateSurvey(data))) {
|
||||
showDialog({
|
||||
title: '无法保存模板',
|
||||
message: '问卷内包含移动端暂未兼容题型/逻辑设置,请至PC端编辑后重新保存。'
|
||||
});
|
||||
return;
|
||||
}
|
||||
const res = await saveTemplates(item.sn, data);
|
||||
if (res.data.code === 200 || res.data.code === 201) {
|
||||
showConfirmDialog({
|
||||
message: '模板保存成功,请前往更多模板页面查看',
|
||||
showCancelButton: false
|
||||
});
|
||||
} else {
|
||||
showFailToast(res.data);
|
||||
}
|
||||
}
|
||||
function clearSurveys() {
|
||||
survey.value = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验问卷是否可以保存为模板
|
||||
* @param data
|
||||
*/
|
||||
async function validateSurvey(survey: SurveyItem): Promise<boolean> {
|
||||
const { data } = await getQuestionList(survey.sn);
|
||||
const { questions, logics } = data.data;
|
||||
const questionValid = questions.every((question: any) => {
|
||||
if (!questionTypeMap.has(question.question_type)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
// 2 自动填写, 3 是逻辑配额
|
||||
const logicValid = logics.every((logic: any) => {
|
||||
if ([0, 1].includes(logic.skip_type)) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
// 判断是否是随机题组/循环题组
|
||||
const surveyValid = !data.data.survey.group_pages?.length > 0;
|
||||
const cycleValid = !data.data?.cycle_pages?.length > 0;
|
||||
return questionValid && logicValid && surveyValid && cycleValid;
|
||||
}
|
||||
|
||||
export {
|
||||
fetchSurveys,
|
||||
loading,
|
||||
finished,
|
||||
survey,
|
||||
total,
|
||||
searchValue,
|
||||
deleteItem,
|
||||
saveTemplate,
|
||||
currentSurvey,
|
||||
requestLoading,
|
||||
fetchSingleSurvey,
|
||||
clearSurveys
|
||||
};
|
||||
|
||||
@@ -37,9 +37,9 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="search">
|
||||
<search-bar placeholder="请输入关键词" v-if="!disableSearch" :value="''" />
|
||||
</div>
|
||||
<!-- <div class="search">-->
|
||||
<!-- <search-bar placeholder="请输入关键词" v-if="!disableSearch" :value="''" />-->
|
||||
<!-- </div>-->
|
||||
<section v-if="currentSurvey" class="survey-container">
|
||||
<!-- 问卷详情部分 -->
|
||||
<van-cell class="survey-item">
|
||||
@@ -103,7 +103,7 @@ onUnmounted(() => {
|
||||
// width: 100%;
|
||||
|
||||
.ai-insight {
|
||||
background-image: url('@/assets/img/home/item-back.png');
|
||||
background: linear-gradient(to bottom, rgb(239, 249, 252) 0, white 20%);
|
||||
background-position: 11% 11%;
|
||||
|
||||
.ai-insight-content {
|
||||
|
||||
@@ -1,84 +1,120 @@
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<!-- 优先去上级传递的数值 -->
|
||||
<section v-for="analysis in questionAnalysis" :key="analysis.stem" class="mt10">
|
||||
<!-- {{ analysis }} -->
|
||||
<!-- 问题标题 -->
|
||||
<el-tag type="success" size="small">
|
||||
{{ questionTypeMap.get(analysis.question_type as number) }}
|
||||
</el-tag>
|
||||
<el-text>{{ analysis.stem }}</el-text>
|
||||
|
||||
<!-- 问题图表部分 -->
|
||||
<chart-msg
|
||||
v-if="showChart.includes(analysis.question_type)"
|
||||
:dimension="analysis.option && analysis.option[0]?.option"
|
||||
:analysis="analysis"
|
||||
/>
|
||||
|
||||
<!-- 问题表格部分 -->
|
||||
<yl-table
|
||||
v-if="getTableData(analysis).length > 0"
|
||||
class="mt10"
|
||||
:props="getTableHeadProps(analysis.head, analysis.option)"
|
||||
:data="getTableData(analysis)"
|
||||
/>
|
||||
<section v-else>
|
||||
<empty-container :error-msg="'本题暂无有效答题数据'" :img-src="emptyImg" />
|
||||
</section>
|
||||
</section>
|
||||
<!-- <section v-else>
|
||||
<empty-container />
|
||||
</section> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 空白容器
|
||||
import EmptyContainer from '@/views/Survey/components/EmptyContainer.vue';
|
||||
import { questionTypeMap } from '@/utils/question/typeMapping';
|
||||
import ChartMsg from '@/components/Analysis/Index.vue';
|
||||
import { getTableData } from './hooks/pieSeries';
|
||||
import YlTable from '@/components/YlTable/Index.vue';
|
||||
import { ref } from 'vue';
|
||||
import { screenLayout } from '@/hooks/browser/useScreen';
|
||||
import emptyImg from '@/assets/img/emptyImg.png';
|
||||
// questionTypeMap 自己去对应
|
||||
const showChart = ref([1, 2, 5, 106, 9, 10]);
|
||||
|
||||
// 接受上级传递的 questionAnalysis 数据
|
||||
const questionAnalysis = defineModel<any[]>('questionAnalysis');
|
||||
|
||||
const { width } = screenLayout();
|
||||
|
||||
// 构建表头
|
||||
const getTableHeadProps = (values: any[], option: any[]): TablePropsType[] => {
|
||||
const head = [];
|
||||
|
||||
if (values && values.length > 0) {
|
||||
values.forEach((item: any) => {
|
||||
if (item.key !== 'option') {
|
||||
head.push({
|
||||
label: item.title,
|
||||
prop: item.key,
|
||||
width: values.length < 4 ? width.value / values.length : 100
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (option.length > 0 && option[0].option) {
|
||||
head.unshift({
|
||||
label: '选项',
|
||||
prop: 'option',
|
||||
width: 150
|
||||
});
|
||||
}
|
||||
return head;
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div style="width: 100%">
|
||||
<!-- 优先去上级传递的数值 -->
|
||||
<section v-for="(analysis, index) in questionAnalysis" :key="analysis.stem" class="mt10">
|
||||
<!-- {{ analysis }} -->
|
||||
<!-- 问题标题 -->
|
||||
<div class="" style="line-height: 30px">
|
||||
<el-tag
|
||||
type="success"
|
||||
size="small"
|
||||
v-if="questionTypeMap.get(analysis.question_type as number)"
|
||||
>
|
||||
{{ questionTypeMap.get(analysis.question_type as number) }}
|
||||
</el-tag>
|
||||
<el-text class="ml10"
|
||||
>{{ parentIndex ? parentIndex : index + 1 }}. {{ analysis.stem }}</el-text
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 问题图表部分 -->
|
||||
<!-- 表格td宽度固定 超出部分滚动-->
|
||||
|
||||
<div v-if="questionTypeMap.get(analysis.question_type as number)">
|
||||
<chart-msg
|
||||
v-if="showChart.includes(analysis.question_type)"
|
||||
:dimension="analysis.option && analysis.option[0]?.option"
|
||||
:analysis="analysis"
|
||||
/>
|
||||
|
||||
<!-- 问题表格部分 -->
|
||||
<yl-table-h
|
||||
class="mt10"
|
||||
v-if="getTableData(analysis).length > 0"
|
||||
:prop="getTableHeadProps(analysis.head, analysis.option)"
|
||||
:data="getTableData(analysis)"
|
||||
></yl-table-h>
|
||||
<!-- <yl-table-->
|
||||
<!-- v-if="getTableData(analysis).length > 0"-->
|
||||
<!-- class="mt10"-->
|
||||
<!-- :props="getTableHeadProps(analysis.head, analysis.option)"-->
|
||||
<!-- :data="getTableData(analysis)"-->
|
||||
<!-- />-->
|
||||
|
||||
<section v-else>
|
||||
<empty-container :error-msg="'本题暂无有效答题数据'" :img-src="emptyImg" />
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div class="empty-text">当前题目结果请至PC端查看,更多题型兼容敬请期待~</div>
|
||||
</div>
|
||||
</section>
|
||||
<!-- <section v-else>
|
||||
<empty-container />
|
||||
</section> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 空白容器
|
||||
import EmptyContainer from '@/views/Survey/components/EmptyContainer.vue';
|
||||
import { questionTypeMap } from '@/utils/question/typeMapping';
|
||||
import ChartMsg from '@/components/Analysis/Index.vue';
|
||||
import { getTableData } from './hooks/pieSeries';
|
||||
import YlTable from '@/components/YlTable/Index.vue';
|
||||
import YlTableH from '@/components/YlTable/yl-table-h.vue';
|
||||
import { ref } from 'vue';
|
||||
import { screenLayout } from '@/hooks/browser/useScreen';
|
||||
import emptyImg from '@/assets/img/emptyImg.png';
|
||||
// questionTypeMap 自己去对应
|
||||
const showChart = ref([1, 2, 5, 106, 9, 10]);
|
||||
|
||||
// 接受上级传递的 questionAnalysis 数据
|
||||
const questionAnalysis = defineModel<any[]>('questionAnalysis');
|
||||
const parentIndex = defineModel<any>('parentIndex');
|
||||
|
||||
const { width } = screenLayout();
|
||||
|
||||
// 构建表头
|
||||
const getTableHeadProps = (values: any[], option: any[]): TablePropsType[] => {
|
||||
const head = [];
|
||||
|
||||
if (values && values.length > 0) {
|
||||
values.forEach((item: any) => {
|
||||
if (item.key !== 'option') {
|
||||
head.push({
|
||||
label: item.title,
|
||||
prop: item.key,
|
||||
width: values.length < 4 ? width.value / values.length : 100,
|
||||
tooltip: false
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (option.length > 0 && option[0].option) {
|
||||
head.unshift({
|
||||
label: '选项',
|
||||
prop: 'option',
|
||||
width: 150
|
||||
});
|
||||
}
|
||||
return head;
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 11px;
|
||||
color: #919191;
|
||||
padding: 20px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,11 +21,12 @@ export const series = ref({
|
||||
}
|
||||
});
|
||||
|
||||
export function formatData(data: any, index: number) {
|
||||
export function formatData(data: any, index: number, isEmpty: boolean = true) {
|
||||
const _series = JSON.parse(JSON.stringify(series.value));
|
||||
// 当内容为单选的时候处理方式
|
||||
if (data.question_type === 1 || data.question_type === 2) {
|
||||
const { option } = data;
|
||||
let { option } = data;
|
||||
|
||||
_series.data = option.map((item: any) => {
|
||||
return {
|
||||
...item,
|
||||
@@ -34,14 +35,26 @@ export function formatData(data: any, index: number) {
|
||||
questionItem: { ...data }
|
||||
};
|
||||
});
|
||||
|
||||
if (isEmpty) {
|
||||
_series.data = _series.data.filter((item) => item.value != 0);
|
||||
}
|
||||
}
|
||||
if (
|
||||
data.question_type === 5
|
||||
|| data.question_type === 9
|
||||
|| data.question_type === 106
|
||||
|| data.question_type === 10
|
||||
data.question_type === 5 ||
|
||||
data.question_type === 9 ||
|
||||
data.question_type === 106 ||
|
||||
data.question_type === 10
|
||||
) {
|
||||
const copyData = setDimensionData(data);
|
||||
let copyData = setDimensionData(data);
|
||||
|
||||
// copyData 删除 value 为0 的数据
|
||||
if (isEmpty) {
|
||||
copyData = copyData.map((item) => {
|
||||
return item.filter((item) => item.value !== 0);
|
||||
});
|
||||
}
|
||||
|
||||
_series.data = copyData[index || 0]?.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
@@ -54,7 +67,7 @@ export function formatData(data: any, index: number) {
|
||||
return _series;
|
||||
}
|
||||
|
||||
export function getTableData(data: any) {
|
||||
export function getTableData(data: any, isEmpty = true) {
|
||||
const analysis = JSON.parse(JSON.stringify(data));
|
||||
const rows = analysis.option || [];
|
||||
return rows.map((rowItem: any) => {
|
||||
|
||||
@@ -20,7 +20,7 @@ const tabs = computed(() => [
|
||||
title: '逻辑配额',
|
||||
props: [
|
||||
{ prop: 'question_title', label: '题号', width: 80 },
|
||||
{ prop: 'logic_text', label: '选项', width: 90 },
|
||||
{ prop: 'text', label: '选项', width: 90 },
|
||||
{ prop: 'sample_number', label: '样本量', width: 90 },
|
||||
{ prop: 'percent', label: '进度', width: 90 }
|
||||
],
|
||||
@@ -74,8 +74,20 @@ const debug = ref(false);
|
||||
<yl-table
|
||||
:data="currentTabs.find((tab) => tab.title === activeTab)?.data"
|
||||
:props="currentTabs.find((tab) => tab.title === activeTab)?.props"
|
||||
:emptyText="`此问卷未设置${activeTab}`"
|
||||
></yl-table>
|
||||
</section>
|
||||
</template>
|
||||
</van-cell>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.logic-info {
|
||||
&::after {
|
||||
content: '';
|
||||
border: none;
|
||||
left: 0;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -11,7 +11,7 @@ import { aiInsightsConfig } from '@/views/Survey/views/Analysis/hooks/useAnalysi
|
||||
{{
|
||||
aiInsightsConfig.info.length !== 0
|
||||
? aiInsightsConfig.info
|
||||
: '我正在为您分析问卷内容,这个过程可能会多花一点点时间,不过马上就好,稍等哦'
|
||||
: '正在为您进行AI洞察,这次需要多分析一会儿才能确保洞察精准度,认真工作的我马上带着结果回来哦~'
|
||||
}}</el-text
|
||||
>
|
||||
</section>
|
||||
|
||||
@@ -52,9 +52,6 @@ export async function postAnalysis(sn: string) {
|
||||
clearInterval(aiInsightsConfig.value.timer);
|
||||
// 获取洞察结果
|
||||
const { data } = await getAnalysis(sn);
|
||||
console.log(data.other.overall_conclusion);
|
||||
|
||||
console.log(data);
|
||||
|
||||
aiAnalysisData.value = data;
|
||||
aiInsightsConfig.value.message = data.other.overall_conclusion;
|
||||
@@ -71,7 +68,5 @@ export async function checkAnalysis(sn: string) {
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export {aiAnalysisData, currentSn };
|
||||
export { aiAnalysisData, currentSn };
|
||||
export { useFetchAnalysis } from '@/hooks/request/useSurvey';
|
||||
|
||||
@@ -836,7 +836,9 @@ const publishQuestion = () => {
|
||||
// 预览
|
||||
const previewQuestion = () => {
|
||||
saveAs(() => {
|
||||
router.push({ name: 'preview', query: { ...route.query } });
|
||||
setTimeout(() => {
|
||||
router.push({ name: 'preview', query: { ...route.query } });
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<!-- <layout />-->
|
||||
<div ref="scrollbar" class="preview-container" v-memo="[currentSn]">
|
||||
<div ref="scrollbar" class="preview-container">
|
||||
<!-- {{ questionsData }}-->
|
||||
<!-- <van-nav-bar :title="getDomString(questionsData?.survey?.title)" left-arrow />-->
|
||||
|
||||
<!-- 进度条 -->
|
||||
@@ -49,7 +50,7 @@
|
||||
|
||||
<!-- 问题 -->
|
||||
<!-- eslint-disable-next-line -->
|
||||
<div class="questions">
|
||||
<div class="questions" v-if="questions">
|
||||
<!-- 提前终止和正常完成 -->
|
||||
<q-last
|
||||
v-if="page === pages.length + 1"
|
||||
@@ -568,7 +569,9 @@ const {
|
||||
translatedText,
|
||||
isTemplate
|
||||
} = storeToRefs(questionStore);
|
||||
questionsData.value = {};
|
||||
|
||||
questionStore.fetchQuestions();
|
||||
// 第一次进入页面清空答案
|
||||
//初始化页面到第一页
|
||||
questionsData.value?.questions && clearAnswer(questionsData.value.questions);
|
||||
@@ -586,13 +589,12 @@ const props = defineProps({
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
// 初始化 isTemplate
|
||||
isTemplate.value = props.isTemplate ? props.isTemplate : route.query.is_template === '1';
|
||||
// 如果当前的 sn 和 之前记录的sn不相同的时候,那么就要重新请求数据
|
||||
if (currentSn.value !== route.query.sn) {
|
||||
questionStore.fetchQuestions();
|
||||
}
|
||||
// if (currentSn.value !== route.query.sn) {
|
||||
|
||||
// }
|
||||
|
||||
// 上一页
|
||||
async function previous() {
|
||||
@@ -619,6 +621,10 @@ async function previous() {
|
||||
|
||||
// 下一页
|
||||
async function next(callbackBeforePage) {
|
||||
// 先清除所有错误
|
||||
questions.value.map((ques) => {
|
||||
ques.error = '';
|
||||
});
|
||||
// 开始校验答案
|
||||
startValidate();
|
||||
// console.log(`click next button`, prevLoading.value || loading.value);
|
||||
@@ -645,10 +651,27 @@ async function answer(callback, callbackBeforePage) {
|
||||
if ((questions.value.length || !questionsData.value.questions.length) && !props.isTemplate) {
|
||||
// 表单验证(当前页)
|
||||
const errors = questions.value.filter((question) => {
|
||||
const { config, answer, question_type: questionType, error } = question;
|
||||
const { config, question_type: questionType, error, answer } = question;
|
||||
console.log(question.answer);
|
||||
// let answer = question.answer.value ? question.answer.value : question.answer;
|
||||
// console.log(answer, questionType, error);
|
||||
// 单独 处理 图文
|
||||
if (questionType === 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
let isError = false;
|
||||
// 如果问题没有答案还有是必须填空的,就往下处理
|
||||
// 2025/4/1新增 : 如果有 error 内容, 同样视为有错误
|
||||
|
||||
// if (config.is_required === 0) {
|
||||
// if (!answer && !answer?.value) {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
console.log(answer, ' isError = true;');
|
||||
|
||||
if (!answer || error) {
|
||||
isError = true;
|
||||
// 各个问题单独处理
|
||||
@@ -671,7 +694,12 @@ async function answer(callback, callbackBeforePage) {
|
||||
// 分类题
|
||||
question.error = translatedText.value.PleaseCategorizeAllOptions;
|
||||
} else if (!question.error) {
|
||||
question.error = translatedText.value.ThisIsARequiredQuestion;
|
||||
console.log(question, 23);
|
||||
if (config.is_required === 1) {
|
||||
question.error = translatedText.value.ThisIsARequiredQuestion;
|
||||
} else {
|
||||
isError = false;
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
answer &&
|
||||
@@ -683,6 +711,11 @@ async function answer(callback, callbackBeforePage) {
|
||||
question.error = translatedText.value.PleaseInputAValue;
|
||||
} else if (answer && questionType === 2) {
|
||||
// 多选题
|
||||
// 选项数量
|
||||
// isError = true;
|
||||
// question.error = translatedText.value.PleaseSelectAtLeastOneOptions(
|
||||
// config.min_select ? config.min_select : 0
|
||||
// );
|
||||
} else if (answer && questionType === 10) {
|
||||
// 矩阵多选题
|
||||
} else if (answer && questionType === 12) {
|
||||
@@ -697,7 +730,9 @@ async function answer(callback, callbackBeforePage) {
|
||||
if (Object.keys(answer).length < (+config.min_select || 0)) {
|
||||
// 选项数量
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptions(config.min_select);
|
||||
question.error = translatedText.value.PleaseSelectAtLeastOneOptions(
|
||||
config.min_select ? config.min_select : 0
|
||||
);
|
||||
}
|
||||
} else if (answer && questionType === 17) {
|
||||
// 恒定总和题
|
||||
@@ -716,11 +751,108 @@ async function answer(callback, callbackBeforePage) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseUploadAtLeastOneFiles(config.min_number);
|
||||
} else if (answer && questionType === 4) {
|
||||
question.error = '';
|
||||
if (!answer.value && config.is_required === 0) {
|
||||
isError = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (answer.value === null) {
|
||||
isError = true;
|
||||
console.log(123123);
|
||||
question.error = translatedText.value.ThisIsARequiredQuestion;
|
||||
return;
|
||||
}
|
||||
// // 矩阵填空题
|
||||
switch (config.text_type) {
|
||||
// 字母
|
||||
case 3:
|
||||
if (
|
||||
!/^[a-zA-Z·~!@#¥%…&*()—\-+={}|《》?:“”【】、;‘’,。`!$^()_<>?:",./;'\\[\]]+$/.test(
|
||||
answer.value
|
||||
)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterEnglishLetters;
|
||||
}
|
||||
break;
|
||||
// 中文
|
||||
case 4:
|
||||
if (
|
||||
!/^(?:[\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(
|
||||
answer.value
|
||||
)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterChineseWords;
|
||||
}
|
||||
break;
|
||||
// 邮箱
|
||||
case 5:
|
||||
if (
|
||||
!/^(([^<>()[\]\\.,;:\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(
|
||||
answer.value
|
||||
)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterACorrectEmail;
|
||||
}
|
||||
break;
|
||||
// 手机号
|
||||
case 6:
|
||||
if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(answer.value)) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterACorrectPhone;
|
||||
}
|
||||
break;
|
||||
// 身份证号
|
||||
case 7:
|
||||
if (
|
||||
!/^[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(
|
||||
answer.value
|
||||
)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterACorrectID;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
console.log(answer.value?.length, config.text_type, config.min);
|
||||
// if (![5, 6, 7].includes(config.text_type)) {
|
||||
if (
|
||||
answer.value?.length < config.min &&
|
||||
![1, 2, 5, 6, 7].includes(config.text_type) &&
|
||||
config.min
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
answer.value?.length > config.max &&
|
||||
![1, 2, 5, 6, 7].includes(config.text_type) &&
|
||||
config.max
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterLessCharacters(config.max);
|
||||
return;
|
||||
}
|
||||
// }
|
||||
if (question.error) isError = true;
|
||||
} else if (answer && questionType === 8) {
|
||||
// 矩阵填空题
|
||||
question.error = '';
|
||||
|
||||
if (!answer && config.is_required === 0) {
|
||||
isError = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Object.keys(answer).forEach((key) => {
|
||||
const value = answer[key];
|
||||
|
||||
switch (config.text_type) {
|
||||
// 字母
|
||||
case 3:
|
||||
@@ -729,6 +861,7 @@ async function answer(callback, callbackBeforePage) {
|
||||
value
|
||||
)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterEnglishLetters;
|
||||
}
|
||||
break;
|
||||
@@ -739,6 +872,7 @@ async function answer(callback, callbackBeforePage) {
|
||||
value
|
||||
)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterChineseWords;
|
||||
}
|
||||
break;
|
||||
@@ -749,12 +883,14 @@ async function answer(callback, callbackBeforePage) {
|
||||
value
|
||||
)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterACorrectEmail;
|
||||
}
|
||||
break;
|
||||
// 手机号
|
||||
case 6:
|
||||
if (!/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(value)) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterACorrectPhone;
|
||||
}
|
||||
break;
|
||||
@@ -765,14 +901,36 @@ async function answer(callback, callbackBeforePage) {
|
||||
value
|
||||
)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterACorrectID;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (!question.error && value.length < config.min && ![1, 2].includes(config.text_type)) {
|
||||
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
|
||||
|
||||
if (![5, 6, 7].includes(config.text_type)) {
|
||||
if (
|
||||
!question.error &&
|
||||
value.length < config.min &&
|
||||
config.min &&
|
||||
![1, 2].includes(config.text_type)
|
||||
) {
|
||||
isError = true;
|
||||
|
||||
question.error = translatedText.value.PleaseEnterMoreThanOneCharacters(config.min);
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
!question.error &&
|
||||
value.length > config.max &&
|
||||
config.max &&
|
||||
![1, 2].includes(config.text_type)
|
||||
) {
|
||||
isError = true;
|
||||
question.error = translatedText.value.PleaseEnterLessCharacters(config.max);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
if (question.error) isError = true;
|
||||
@@ -1315,6 +1473,7 @@ function clearAnswer(questions) {
|
||||
|
||||
.preview-container {
|
||||
overflow: scroll;
|
||||
height: 100%;
|
||||
max-height: calc(100vh - var(--sticky-top-height) - 65px - 40px);
|
||||
|
||||
//min-height: calc(100vh - 100px);
|
||||
|
||||
@@ -34,18 +34,21 @@ const completionValue = ref<string>(answer.value?.value ?? '');
|
||||
watch(
|
||||
() => completionValue.value,
|
||||
(value) => {
|
||||
console.log(value, 'update');
|
||||
|
||||
// 答案校验,生成最终答案
|
||||
const { isError } = validateCompletion(question.value, question.value.config!, String(value));
|
||||
console.log(`isError, question`, isError, question.value);
|
||||
if (isError) {
|
||||
// 有错误就不更新
|
||||
question.value.answer = void 0;
|
||||
return;
|
||||
}
|
||||
// if (isError) {
|
||||
// 有错误就不更新
|
||||
question.value.answer = void 0;
|
||||
// return;
|
||||
// }
|
||||
|
||||
const res = generateAnswer(value);
|
||||
answer.value = res;
|
||||
question.value!.answer = res;
|
||||
|
||||
// answer emit 提交失效
|
||||
// emit('changeAnswer', res);
|
||||
}
|
||||
@@ -56,6 +59,8 @@ watch(
|
||||
* @param answer {string}
|
||||
*/
|
||||
function generateAnswer(answer: string) {
|
||||
console.log(answer, 'asjd');
|
||||
|
||||
return { value: answer };
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,16 +3,17 @@ import FileUpload from '@/views/Design/components/Questions/FileUpload.vue';
|
||||
const questionIndex = defineModel<number>('questionIndex', { default: NaN });
|
||||
const answerIndex = computed(() => question.value.title);
|
||||
const question = defineModel<question>('question', { default: () => {} });
|
||||
import { answer } from '@/views/Design/components/Questions/hooks/useFileUploadHooks';
|
||||
// import { answer } from '@/views/Design/components/Questions/hooks/useFileUploadHooks';
|
||||
import { computed, watch } from 'vue';
|
||||
|
||||
const emit = defineEmits(['changeAnswer']);
|
||||
watch(answer, () => {
|
||||
// 暂时先将答案挂到 question,后续需要优化
|
||||
question.value.answer = answer.value;
|
||||
// emit('changeAnswer', answer.value);
|
||||
// console.log(`question`, question.value);
|
||||
});
|
||||
// watch(answer, () => {
|
||||
// // 暂时先将答案挂到 question,后续需要优化
|
||||
// question.value.answer = answer.value;
|
||||
// console.log(question.value.answer);
|
||||
// // emit('changeAnswer', answer.value);
|
||||
// // console.log(`question`, question.value);
|
||||
// });
|
||||
</script>
|
||||
<template>
|
||||
<!-- 文件上传题 -->
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import TextWithImages from '@/views/Design/components/Questions/TextWithImages.vue';
|
||||
import { computed } from 'vue';
|
||||
import { computed, watch } from 'vue';
|
||||
|
||||
// 问题
|
||||
const question = defineModel<question>('question', { default: {} });
|
||||
// question 序号
|
||||
const answerIndex = computed(() => question.value?.title ?? 0);
|
||||
// 答案
|
||||
const answer = defineModel('answer', { default: {} });
|
||||
// answer 提供默认值
|
||||
answer.value = {};
|
||||
const answer = defineModel('answer');
|
||||
// setTimeout(() => {
|
||||
// answer.value = '123';
|
||||
// }, 300);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
<div class="question-conclude-wrapper">
|
||||
<div class="content">
|
||||
<div v-if="isTemplate" v-html="survey?.success_end_content" />
|
||||
|
||||
<div v-if="code === 20004" v-html="survey?.screening_end_content" />
|
||||
<div v-else-if="code === 20004" v-html="survey?.screening_end_content" />
|
||||
<div v-else-if="code === 20011" v-html="survey?.success_end_content" />
|
||||
<div v-else-if="code === 20016" v-html="survey?.quota_end_content" />
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,10 @@ import { validateEmail } from '@/views/Survey/views/Preview/components/questions
|
||||
import { validatePhone } from '@/views/Survey/views/Preview/components/questions/validate/validatePhone';
|
||||
import { validateIDCard } from '@/views/Survey/views/Preview/components/questions/validate/validateIDCard';
|
||||
import type { ITranslatedText } from '@/views/Survey/views/Preview/components/questions/validate/validateChineseLetter';
|
||||
import { validateMinLength,validateMaxLength } from '@/views/Survey/views/Preview/components/questions/validate/validateStringLength';
|
||||
import {
|
||||
validateMinLength,
|
||||
validateMaxLength
|
||||
} from '@/views/Survey/views/Preview/components/questions/validate/validateStringLength';
|
||||
|
||||
/**
|
||||
* 生成对应的语言文字
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('validateEmail 函数测试', () => {
|
||||
'test123@example.com'
|
||||
];
|
||||
|
||||
validEmails.forEach(email => {
|
||||
validEmails.forEach((email) => {
|
||||
const result = validateEmail(email, mockTranslatedText);
|
||||
expect(result).toBe('');
|
||||
});
|
||||
@@ -40,7 +40,7 @@ describe('validateEmail 函数测试', () => {
|
||||
'test@example.com@example.com'
|
||||
];
|
||||
|
||||
invalidEmails.forEach(email => {
|
||||
invalidEmails.forEach((email) => {
|
||||
const result = validateEmail(email, mockTranslatedText);
|
||||
expect(result).toBe(mockTranslatedText.PleaseEnterACorrectEmail);
|
||||
});
|
||||
|
||||
@@ -19,7 +19,7 @@ describe('validateIDCard 函数测试', () => {
|
||||
'11010119900307095x'
|
||||
];
|
||||
|
||||
validIDs.forEach(id => {
|
||||
validIDs.forEach((id) => {
|
||||
const result = validateIDCard(id, mockTranslatedText);
|
||||
expect(result).toBe('');
|
||||
});
|
||||
@@ -38,7 +38,7 @@ describe('validateIDCard 函数测试', () => {
|
||||
'12345678901234567' // 格式错误
|
||||
];
|
||||
|
||||
invalidIDs.forEach(id => {
|
||||
invalidIDs.forEach((id) => {
|
||||
const result = validateIDCard(id, mockTranslatedText);
|
||||
expect(result).toBe(mockTranslatedText.PleaseEnterACorrectID);
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('validatePhone 函数测试', () => {
|
||||
'008613800138000'
|
||||
];
|
||||
|
||||
validPhones.forEach(phone => {
|
||||
validPhones.forEach((phone) => {
|
||||
const result = validatePhone(phone, mockTranslatedText);
|
||||
expect(result).toBe('');
|
||||
});
|
||||
@@ -38,7 +38,7 @@ describe('validatePhone 函数测试', () => {
|
||||
'00112345678901' // 非中国号码
|
||||
];
|
||||
|
||||
invalidPhones.forEach(phone => {
|
||||
invalidPhones.forEach((phone) => {
|
||||
const result = validatePhone(phone, mockTranslatedText);
|
||||
expect(result).toBe(mockTranslatedText.PleaseEnterACorrectPhone);
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('validateStringLength 函数测试', () => {
|
||||
it('当字符串长度大于最小值时,应返回 undefined', () => {
|
||||
const answer = '测试字符串';
|
||||
const config: ICompletionConfig = { min: 3, max: 20, text_type: 0 };
|
||||
|
||||
|
||||
const result = validateMinLength(answer, false, config, mockTranslatedText);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
@@ -28,7 +28,7 @@ describe('validateStringLength 函数测试', () => {
|
||||
it('当字符串长度小于最小值时,应返回错误信息', () => {
|
||||
const answer = '测试';
|
||||
const config: ICompletionConfig = { min: 5, max: 20, text_type: 0 };
|
||||
|
||||
|
||||
const result = validateMinLength(answer, false, config, mockTranslatedText);
|
||||
expect(result).toEqual({
|
||||
isError: true,
|
||||
@@ -39,7 +39,7 @@ describe('validateStringLength 函数测试', () => {
|
||||
it('当 min 为空字符串时,应返回 undefined', () => {
|
||||
const answer = '';
|
||||
const config: ICompletionConfig = { min: '', max: 20, text_type: 0 };
|
||||
|
||||
|
||||
const result = validateMinLength(answer, false, config, mockTranslatedText);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
@@ -47,7 +47,7 @@ describe('validateStringLength 函数测试', () => {
|
||||
it('当已有错误时,应返回 undefined', () => {
|
||||
const answer = '测试';
|
||||
const config: ICompletionConfig = { min: 5, max: 20, text_type: 0 };
|
||||
|
||||
|
||||
const result = validateMinLength(answer, true, config, mockTranslatedText);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
@@ -56,10 +56,10 @@ describe('validateStringLength 函数测试', () => {
|
||||
const answer = '123';
|
||||
const config1: ICompletionConfig = { min: 5, max: 20, text_type: 1 };
|
||||
const config2: ICompletionConfig = { min: 5, max: 20, text_type: 2 };
|
||||
|
||||
|
||||
const result1 = validateMinLength(answer, false, config1, mockTranslatedText);
|
||||
const result2 = validateMinLength(answer, false, config2, mockTranslatedText);
|
||||
|
||||
|
||||
expect(result1).toBeUndefined();
|
||||
expect(result2).toBeUndefined();
|
||||
});
|
||||
@@ -69,7 +69,7 @@ describe('validateStringLength 函数测试', () => {
|
||||
it('当字符串长度小于最大值时,应返回 undefined', () => {
|
||||
const answer = '测试字符串';
|
||||
const config: ICompletionConfig = { min: 3, max: 20, text_type: 0 };
|
||||
|
||||
|
||||
const result = validateMaxLength(answer, false, config, mockTranslatedText);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
@@ -77,7 +77,7 @@ describe('validateStringLength 函数测试', () => {
|
||||
it('当字符串长度大于等于最大值时,应返回错误信息', () => {
|
||||
const answer = '这是一个非常长的测试字符串,超过了最大长度限制';
|
||||
const config: ICompletionConfig = { min: 5, max: 20, text_type: 0 };
|
||||
|
||||
|
||||
const result = validateMaxLength(answer, false, config, mockTranslatedText);
|
||||
expect(result).toEqual({
|
||||
isError: true,
|
||||
@@ -88,7 +88,7 @@ describe('validateStringLength 函数测试', () => {
|
||||
it('当 max 为空字符串时,应返回 undefined', () => {
|
||||
const answer = '测试字符串';
|
||||
const config: ICompletionConfig = { min: 3, max: '', text_type: 0 };
|
||||
|
||||
|
||||
const result = validateMaxLength(answer, false, config, mockTranslatedText);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
@@ -96,7 +96,7 @@ describe('validateStringLength 函数测试', () => {
|
||||
it('当已有错误时,应返回 undefined', () => {
|
||||
const answer = '这是一个非常长的测试字符串,超过了最大长度限制';
|
||||
const config: ICompletionConfig = { min: 5, max: 20, text_type: 0 };
|
||||
|
||||
|
||||
const result = validateMaxLength(answer, true, config, mockTranslatedText);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
@@ -105,10 +105,10 @@ describe('validateStringLength 函数测试', () => {
|
||||
const answer = '123456789012345678901234567890';
|
||||
const config1: ICompletionConfig = { min: 5, max: 20, text_type: 1 };
|
||||
const config2: ICompletionConfig = { min: 5, max: 20, text_type: 2 };
|
||||
|
||||
|
||||
const result1 = validateMaxLength(answer, false, config1, mockTranslatedText);
|
||||
const result2 = validateMaxLength(answer, false, config2, mockTranslatedText);
|
||||
|
||||
|
||||
expect(result1).toBeUndefined();
|
||||
expect(result2).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -106,7 +106,7 @@ function validateMatrixCheckbox(
|
||||
: translatedText.PleaseSelectLessOptionsPerLine(maxSelect, rowIndex);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(rows.options!.length, answer.length);
|
||||
// 检测所有的答案是否正常选择
|
||||
if (errorMessage.length === 0 && rows.options!.length > answer.length) {
|
||||
errorMessage = translatedText.PleaseSelectAllRows;
|
||||
|
||||
@@ -19,7 +19,7 @@ function validateMultiSelectQuestion(
|
||||
|
||||
// 多选题
|
||||
// 如果答案数量小于最小选择的数量
|
||||
if (answer.length < Number(config.min_select)) {
|
||||
if (answer.length < Number(config.min_select) && config.min_select) {
|
||||
// 选项数量
|
||||
// let options: questionOptionType[] = [];
|
||||
// 疑问脸
|
||||
@@ -38,7 +38,7 @@ function validateMultiSelectQuestion(
|
||||
} else {
|
||||
errorMessage = '';
|
||||
}
|
||||
} else if (answer.length > Number(config.max_select)) {
|
||||
} else if (answer.length > Number(config.max_select) && config.max_select) {
|
||||
errorMessage = translatedText.PleaseSelectNoMoreOptions(config.max_select);
|
||||
} else if (answer.findIndex((value) => !value) !== -1) {
|
||||
errorMessage = translatedText.PleaseInputAValue;
|
||||
|
||||
@@ -22,7 +22,7 @@ export function validateMinLength(
|
||||
// 如果包含相应的 text_type, 也不处理,1是整数,2是小数
|
||||
if ([1, 2].includes(config.text_type)) return;
|
||||
|
||||
if (answer.length > Number(config.min)) return;
|
||||
if (answer.length >= Number(config.min)) return;
|
||||
return {
|
||||
isError: true,
|
||||
errorMessage: translatedText.PleaseEnterMoreCharacters(config.min)
|
||||
|
||||
@@ -102,6 +102,10 @@ export const language = {
|
||||
en: 'Please enter a valid ID card number.',
|
||||
zh: '请输入正确的身份证号。'
|
||||
},
|
||||
PleaseEnterMoreThanOneCharacters: {
|
||||
en: (count) => `Please enter more than ${count} character${count > 1 ? 's' : ''}.`,
|
||||
zh: (count) => `请输入大于${count}个字符。`
|
||||
},
|
||||
PleaseEnterLessCharacters: {
|
||||
en: (count) => `Please enter less than ${count} character${count > 1 ? 's' : ''}.`,
|
||||
zh: (count) => `请输入小于${count}个字符。`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { test, spec } from "vitest";
|
||||
import { getLanguage } from "../language";
|
||||
import { test, spec } from 'vitest';
|
||||
import { getLanguage } from '../language';
|
||||
|
||||
test("检测 language 列表", () => {
|
||||
const res = getLanguage(['zh'])
|
||||
test('检测 language 列表', () => {
|
||||
const res = getLanguage(['zh']);
|
||||
console.log(res);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -143,7 +143,7 @@ function downLoadImg() {
|
||||
const { title, url } = publishInfo.value.download_url;
|
||||
if (appBridge.isInReactNative()) {
|
||||
appBridge.save2Album(url, () => {
|
||||
showToast('二维码下载成功,请打系统相册查看');
|
||||
showToast('二维码下载成功,请打开系统相册查看');
|
||||
});
|
||||
} else {
|
||||
const link = document.createElement('a');
|
||||
|
||||
@@ -11,7 +11,7 @@ import legacy from '@vitejs/plugin-legacy';
|
||||
// shift + alt 快速定位到对应组件
|
||||
import { codeInspectorPlugin } from 'code-inspector-plugin';
|
||||
// 导入 dev tools
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools';
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
// 接收 mode 参数
|
||||
@@ -24,6 +24,7 @@ export default defineConfig(({ mode }) => {
|
||||
return {
|
||||
// 必须 return 配置对象
|
||||
server: {
|
||||
allowedHosts: ['yiligpt.x.digitalyili.com'],
|
||||
host: '0.0.0.0',
|
||||
port: 3000,
|
||||
proxy: {
|
||||
|
||||