feat(api): 新增通用上传文件方法

- 添加 uploadFileWidthSn 和 uploadFileWidthSnForAnswer 方法用于上传文件
- 实现 getOssInfo 方法获取 OSS 信息
- 添加 cosUpload3D 和 cosUpload 方法上传文件到 COS- 实现 docuHousecosUpload 方法上传文件到阿里云和文档库
- 优化文件命名和错误处理
This commit is contained in:
陈昱达
2025-03-16 14:43:55 +08:00
parent 6f3c89e15e
commit d015da898d
15 changed files with 462 additions and 119 deletions

13
components.d.ts vendored
View File

@@ -2,15 +2,21 @@
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {};
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
Contenteditable: typeof import('./src/components/contenteditable.vue')['default']
ElButton: typeof import('element-plus/es')['ElButton']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSpace: typeof import('element-plus/es')['ElSpace']
ElText: typeof import('element-plus/es')['ElText']
RichText: typeof import('./src/components/RichText.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
@@ -32,8 +38,13 @@ declare module 'vue' {
VanRadio: typeof import('vant/es')['Radio']
VanRadioGroup: typeof import('vant/es')['RadioGroup']
VanRow: typeof import('vant/es')['Row']
VanSearch: typeof import('vant/es')['Search']
VanStepper: typeof import('vant/es')['Stepper']
VanSwitch: typeof import('vant/es')['Switch']
VanTab: typeof import('vant/es')['Tab']
VanTabbar: typeof import('vant/es')['Tabbar']
VanTabbarItem: typeof import('vant/es')['TabbarItem']
VanTabs: typeof import('vant/es')['Tabs']
YLCascader: typeof import('./src/components/YLCascader.vue')['default']
YLInput: typeof import('./src/components/YLInput.vue')['default']
YLPicker: typeof import('./src/components/YLPicker.vue')['default']

147
src/api/common.js Normal file
View File

@@ -0,0 +1,147 @@
import request from '@/utils/request';
import createCOS from '@/utils/txyunCosUpLoad.js';
import { uploadDocumentFile } from './upload/index.js';
import router from '../router/index';
import { computed } from 'vue';
import { shrinkImage } from 'shrinkpng';
const sn = computed(() => router.currentRoute.value.query.sn || 'default');
export default class CommonApi {
static uploadFileWidthSn(file, sn) {
const formData = new FormData();
formData.append('file', file);
formData.append('sn', sn || '');
return request({
url: '/console/upload_file',
method: 'post',
data: formData
});
}
static uploadFileWidthSnForAnswer(file, sn) {
const formData = new FormData();
formData.append('file', file);
return request({
url: `/answer/upload_file/${sn}`,
method: 'post',
data: formData
});
}
/** OssInfo */
static getOssInfo() {
return request({
url: '/console/files/credentials'
});
}
static async cosUpload3DCompress(file, quality = 20) {
const _file = await shrinkImage(file, { quality });
return await this.cosUpload3D(_file);
}
static async cosUpload3D(file, defaultName = '') {
let name = defaultName?.replace?.(/[\s+]/g, '_') || '';
const getRandomFileName = () => {
return `3D/upload/${new Date().getTime()}_${Math.floor(Math.random() * 1000)}`;
};
name = name || getRandomFileName();
const { data } = await CommonApi.getOssInfo();
const param = {
region: data.Region,
host: data.Host,
sessionToken: data.sessionToken,
tmpSecretId: data.tmpSecretId,
tmpSecretKey: data.tmpSecretKey,
bucket: data.Bucket,
startTime: data.startTime,
expiredTime: data.expiredTime,
name: `${data.prefix}/${name}`,
file
};
try {
const location = await createCOS(param);
return {
url: `https://${location}`,
name
};
} catch (error) {
return {
url: '',
name: ''
};
}
}
/**
* 上传文件
* @param {文件file} file
* @returns {url:"",name:'fileName'}
*/
static async cosUpload(file, fileName) {
let name = fileName?.replace?.(/[\s+]/g, '_') || '';
const getRandomFileName = (name) => {
return `survey/${sn.value}/${new Date().getTime()}-${Math.floor(Math.random() * 1000)}-${name}`;
};
/* eslint-disable no-useless-escape */
const reg = /\\|\/|\?|\|\*|"|“|”|'|||<|>|{|}|\[|\]|\【|\】||:|、|\^|\$|!|~|`|\s|\+/g;
name =
name ||
getRandomFileName(file?.name?.replace(reg, '') ?? '' ?? `${new Date().getTime()}.png`);
const res = await CommonApi.getOssInfo();
/* eslint-enable no-useless-escape */
const { data } = res.data;
const param = {
region: data.Region,
host: data.Host,
sessionToken: data.sessionToken,
tmpSecretId: data.tmpSecretId,
tmpSecretKey: data.tmpSecretKey,
bucket: data.Bucket,
startTime: data.startTime,
expiredTime: data.expiredTime,
name: `${data.prefix}/${name}`,
file
};
try {
const location = await createCOS(param);
return {
url: `https://${location}`,
name
};
} catch (error) {
return {
url: '',
name: ''
};
}
}
/**
* 上传文件到阿里云和文档库
* @param {文件file} file
* @param {问卷id} sn
* @param {1=问卷设计,2=数据分析,3=问卷预览,4=数据明细,5=图表分析,6=高级分析,7=市场模拟} importType
* @returns {url:"",name:'fileName'}
*/
static async docuHousecosUpload(file, sn, importType, startFileName = 'survey') {
// eslint-disable-next-line standard/no-callback-literal
const reg = /\\|\/|\?||\*|"|“|”|'|||<|>|{|}|\[|\]|【|】||:|、|\^|\$|!|~|`|\s|\+/g;
// eslint-disable-next-line standard/no-callback-literal
const fileName = `${startFileName}/${new Date().getTime()}_${Math.floor(Math.random() * 1000)}_${
file?.name?.replace(reg, '') ?? '' ?? `${new Date().getTime()}.png`
}`;
try {
const result = await this.cosUpload(file, fileName);
uploadDocumentFile({
url: JSON.stringify([{ name: file.name, url: result.url, size: file.size }]),
parent_sn: sn?.value || sn,
import_type: importType || 1
});
return result;
} catch (error) {
// error
}
}
}

8
src/api/upload/index.js Normal file
View File

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

View File

@@ -1,9 +1,11 @@
<template>
<div class="flex contenteditable align-center space-between" :class="className">
<p
:id="'editor' + id"
ref="editor"
:contenteditable="active"
class="van-field contenteditable-content"
@focus="onFocus"
v-html="modelValue"
></p>
<div class="right-icon ml10">
@@ -13,13 +15,15 @@
<div v-if="showAction && active" ref="editorAction" class="editor-action">
<button v-for="item in actions" :key="item.name" @click="funEvent(item, $event)">
{{ item.label }}
<van-icon class-prefix="mobilefont" :name="item.icon"></van-icon>
</button>
</div>
</template>
<script setup>
import { defineEmits, ref, onMounted, watch } from 'vue';
import { v4 as uuidv4 } from 'uuid';
import CommonApi from '@/api/common.js';
const props = defineProps({
modelValue: {
@@ -36,6 +40,8 @@ const props = defineProps({
}
});
const id = ref(uuidv4());
const editor = ref(null);
const editorAction = ref(null);
const emit = defineEmits(['update:modelValue', 'blur']);
@@ -43,8 +49,9 @@ const save = (e) => {
emit('update:modelValue', e.innerHTML);
};
const savedRange = ref(null);
const functions = {
// todo 点击按钮之后 如何判断 才能让按钮再次点击 不消失 获取重新聚焦 再次选中文本?
boldModern: () => {
document.execCommand('bold', true, null);
},
@@ -53,13 +60,45 @@ const functions = {
},
italic: () => {
document.execCommand('italic', false, null);
},
uploadImage: async() => {
// 保存当前光标位置
savedRange.value = saveSelection();
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.click();
fileInput.onchange = async(e) => {
const [file] = e.target.files;
if (!file) return;
if (file.size > 2 * 1024 * 1024) {
console.error('文件大小不能超过2M');
return;
}
const data = await CommonApi.cosUpload(file);
const img = document.createElement('img');
img.src = data.url;
img.onload = () => {
const scale = img.naturalHeight / 50;
const width = (img.naturalWidth / scale).toFixed(0);
const height = 50;
const maxWidth = img.naturalWidth;
const maxHeight = img.naturalHeight;
const style = `style="width:${width}px;height:${height}px;max-width:${maxWidth}px;max-height:${maxHeight}px"`;
insertImageAtCaret(`<img src=${data.url} ${style}/>`);
// 恢复光标位置
restoreSelection(savedRange.value);
};
};
}
};
const funEvent = (item) => {
// 保持焦点在编辑器
const selection = window.getSelection();
selection.getRangeAt(0);
functions[item.fun]();
};
@@ -82,21 +121,22 @@ const actions = [
{
label: '加粗',
fun: 'boldModern',
icon: ''
icon: 'jiacu'
},
{
label: '下划线',
fun: 'underLine',
icon: ''
icon: 'xiahuaxian'
},
{
label: '图片上传',
fun: 'uploadImage',
icon: ''
icon: 'tupian'
},
{
label: '倾斜',
fun: 'italic'
fun: 'italic',
icon: 'qingxie'
}
];
const checkContains = (element, target) => {
@@ -112,8 +152,6 @@ onMounted(() => {
showAction.value = true;
});
// 如果点击了 editor 与 editorAction 其他地方就触发保存
document.addEventListener('click', (e) => {
if (!editor.value || !editorAction.value) return;
@@ -127,19 +165,58 @@ onMounted(() => {
emit('blur', editor.value);
}
});
// editor.value.addEventListener('blur', () => {
// setTimeout(() => {
// showAction.value = false;
// });
// });
document.addEventListener('resize', () => {
showAction.value = false;
});
});
// onBeforeUnmount(() => {
// editor.value.removeEventListener('resize', handleResize);
// });
const onFocus = (e) => {
// 阻止
e.preventDefault();
e.stopPropagation();
console.log('Editor focused');
};
// 保存当前光标位置
const saveSelection = () => {
const selection = window.getSelection();
if (selection.rangeCount === 0) return null;
return selection.getRangeAt(0);
};
// 恢复光标位置
const restoreSelection = (range) => {
if (!range) return;
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
};
// 在光标位置插入图片
const insertImageAtCaret = (html) => {
const selection = window.getSelection();
if (selection.rangeCount === 0) return;
const range = selection.getRangeAt(0);
range.deleteContents();
const div = document.createElement('div');
div.innerHTML = html;
const frag = document.createDocumentFragment();
for (let child = div.firstChild; child; child = div.firstChild) {
frag.appendChild(child);
}
range.insertNode(frag);
range.setStartAfter(frag);
range.collapse(true);
selection.removeAllRanges();
selection.addRange(range);
};
</script>
<style scoped lang="scss">
@@ -166,7 +243,12 @@ onMounted(() => {
& button {
border: none;
background: #fff;
color: #000;
outline: none;
}
button + button {
margin-left: 10px;
}
}
</style>

View File

@@ -4,11 +4,11 @@
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src:
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix')
format('embedded-opentype'),
format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont')
format('svg');
format('svg');
}
.logo {

View File

@@ -14,6 +14,22 @@
-moz-osx-font-smoothing: grayscale;
}
.mobilefont-jiacu::before {
content: '\e71d';
}
.mobilefont-qingxie::before {
content: '\e71e';
}
.mobilefont-xiahuaxian::before {
content: '\e720';
}
.mobilefont-tupian::before {
content: '\e730';
}
.mobilefont-del1::before {
content: '\e637';
}

Binary file not shown.

View File

@@ -25,7 +25,7 @@ export const QUESTION_TYPE = {
MATRIX_RADIO: 9,
contains(item) {
return Object.keys(this).some((key) => this[key] == item);
return Object.keys(this).some((key) => this[key] === item);
}
};

View File

@@ -368,39 +368,39 @@ export function isCross(range1, range2) {
const isSibling = isLeft || isRight;
// 逻辑包含循环
const contain =
(isPlainSequence &&
(((isNullish(start2) || isSequence(judge, start2, start1)) &&
(isNullish(end2) || isSequence(judge, end2, start1))) ||
((isNullish(start2) || isSequence(start1, start2, end1)) &&
(isNullish(end2) || isSequence(start1, end2, end1))))) ||
(!isPlainSequence &&
(judge < start1
? ((isNullish(start2) || isSequence(judge, start2, start1)) &&
(isNullish(end2) || isSequence(judge, end2, start1))) ||
((isNullish(start2) || isSequence(start1, start2, end1)) &&
(isNullish(end2) || isSequence(start1, end2, end1)))
: ((isNullish(start2) || isSequence(start1, start2, judge)) &&
(isNullish(end2) || isSequence(start1, end2, judge))) ||
((isNullish(start2) || isSequence(judge, start2, end1)) &&
(isNullish(end2) || isSequence(judge, end2, end1)))));
const contain
= (isPlainSequence
&& (((isNullish(start2) || isSequence(judge, start2, start1))
&& (isNullish(end2) || isSequence(judge, end2, start1)))
|| ((isNullish(start2) || isSequence(start1, start2, end1))
&& (isNullish(end2) || isSequence(start1, end2, end1)))))
|| (!isPlainSequence
&& (judge < start1
? ((isNullish(start2) || isSequence(judge, start2, start1))
&& (isNullish(end2) || isSequence(judge, end2, start1)))
|| ((isNullish(start2) || isSequence(start1, start2, end1))
&& (isNullish(end2) || isSequence(start1, end2, end1)))
: ((isNullish(start2) || isSequence(start1, start2, judge))
&& (isNullish(end2) || isSequence(start1, end2, judge)))
|| ((isNullish(start2) || isSequence(judge, start2, end1))
&& (isNullish(end2) || isSequence(judge, end2, end1)))));
// 循环存在封闭区间,并且循环包含逻辑
const contained =
!isNullish(start2) &&
!isNullish(end2) &&
const contained
= !isNullish(start2)
&& !isNullish(end2)
// [judge, start1, end1];
((isPlainSequence && start2 <= judge && end1 <= end2) ||
&& ((isPlainSequence && start2 <= judge && end1 <= end2)
// [judge, start1, end1];
// [start1, judge, end1];
(!isPlainSequence && start2 <= start1 && start2 <= judge && end1 <= end2));
|| (!isPlainSequence && start2 <= start1 && start2 <= judge && end1 <= end2));
// 循环不存在封闭区间
const unCircled =
(!isNullish(start2) &&
isNullish(end2) &&
((isPlainSequence && start2 === judge) || (!isPlainSequence && judge < start1)
const unCircled
= (!isNullish(start2)
&& isNullish(end2)
&& ((isPlainSequence && start2 === judge) || (!isPlainSequence && judge < start1)
? start2 === judge
: start2 === start1)) ||
(isNullish(start2) && !isNullish(end2) && end2 === end1);
: start2 === start1))
|| (isNullish(start2) && !isNullish(end2) && end2 === end1);
return !(isSibling || contain || contained || unCircled);
}
@@ -420,13 +420,13 @@ function isSequence(s1, s2, s3, equal) {
* @param store
*/
export function updateNewQuestionsByLoopingEffect(quesSaveParam, store) {
const { questionInfoBeforeModified = {}, questionInfo = {} } =
JSON.parse(JSON.stringify(store.state.common)) || {};
const { questionInfoBeforeModified = {}, questionInfo = {} }
= JSON.parse(JSON.stringify(store.state.common)) || {};
const oldPages = questionInfoBeforeModified.survey.pages;
const newQuestions = questionInfo.questions;
const newPages = questionInfo.survey.is_one_page_one_question
? questionInfo.questions.filter((i) => i.question_index).map((i, idx) => [i.question_index])
? questionInfo.questions.filter((i) => i.question_index).map((i) => [i.question_index])
: questionInfo.survey.pages;
const cycles = questionInfo.cycle_pages || [];

View File

@@ -33,7 +33,9 @@ function showModal(options) {
.then(() => {
// confirm();
})
.catch(() => {});
.catch(() => {
// catch();
});
}
/**
@@ -41,13 +43,13 @@ function showModal(options) {
* @param {*} data
* @returns
*/
const canPlanetPublishPSM = function (data) {
const canPlanetPublishPSM = function(data) {
let isFb = true;
let message = '';
let title = '题目设置未完成';
const incompleteQuestionList = [];
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (s.question_type === 101 && s.config.price_gradient.length <= 0) {
isFb = false;
message = 'psm题目未完成设置请设置价格区间后投放';
@@ -72,15 +74,15 @@ const canPlanetPublishPSM = function (data) {
* @param {*} data
* @returns
*/
const canPlanetPublishMxdAndHotArea = function (data) {
const canPlanetPublishMxdAndHotArea = function(data) {
let isFb = true;
let message = '';
const qSteams = [];
const incompleteQuestionList = [];
let type = 0;
let title = '题目设置未完成';
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (s.question_type === 105 && s.config.design_version <= 0) {
isFb = false;
message = 'maxdiff题目未完成设置请生成设计后投放';
@@ -119,21 +121,23 @@ const canPlanetPublishMxdAndHotArea = function (data) {
* @param {*} data
* @returns
*/
const canPlanetPublish3D = function (data) {
const canPlanetPublish3D = function(data) {
{
let canFB = true;
let message = '';
const qSteams = [];
let title = '';
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (QUESTION_TYPE.contains(s.question_type)) {
try {
if (s.config.is_three_dimensions && !s.config.scene) {
canFB = false;
qSteams.push(`(${s.title})`);
}
} catch (error) {}
} catch (error) {
// eslint-disable-next-line no-eval
}
}
});
@@ -154,15 +158,15 @@ const canPlanetPublish3D = function (data) {
let message = '';
const qSteams = [];
let title = '';
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (QUESTION_TYPE.contains(s.question_type)) {
try {
if (s.config.is_three_dimensions && s.config.is_binding_goods) {
const wares = [];
const _sceneInformation = s.config.scene_information;
const sceneInformation =
typeof _sceneInformation === 'string'
const sceneInformation
= typeof _sceneInformation === 'string'
? JSON.parse(_sceneInformation)
: _sceneInformation;
sceneInformation.shelves.forEach((shelf) => {
@@ -174,7 +178,10 @@ const canPlanetPublish3D = function (data) {
const options = s.options.flat();
s.associate.forEach((ass) => {
const question = data.questions.find((q) => q.question_index == ass.question_index);
// eslint-disable-next-line no-eval
const question = data.questions.find(
(q) => q.question_index === ass.question_index
);
if (!question) return;
options.push(...question.options.flat());
});
@@ -184,7 +191,9 @@ const canPlanetPublish3D = function (data) {
qSteams.push(`(${s.title})`);
}
}
} catch (error) {}
} catch (error) {
// error
}
}
});
@@ -207,14 +216,14 @@ const canPlanetPublish3D = function (data) {
* @param {*} data
* @returns
*/
const canPlanetPublishImage = function (data) {
const canPlanetPublishImage = function(data) {
{
let canFB = true;
let message = '';
const qSteams = [];
let title = '';
data.questions &&
data.questions.forEach((s) => {
data.questions
&& data.questions.forEach((s) => {
if (s.question_type === 13) {
try {
if (s.options.length <= 0 || s.options.some((y) => y.length <= 0)) {
@@ -284,9 +293,10 @@ function canPublishRandom(data, publishType) {
const publishStr = ['', '预览', '投放'][publishType] || '投放';
const errors = [];
// eslint-disable-next-line no-eval
const randomList = data?.survey?.group_pages || [];
randomList.forEach((random, randomIndex) => {
randomList.forEach((random) => {
const list = random.list || [];
// 每一个随机,至少要有两个随机题组
@@ -308,8 +318,8 @@ function canPublishRandom(data, publishType) {
if (!isValidated) {
errors.push({
message:
field.message ||
`请填写"${random.title}"中第${index + 1}组"随机题组"的"${field.name}"`
field.message
|| `请填写"${random.title}"中第${index + 1}组"随机题组"的"${field.name}"`
});
}
});
@@ -369,11 +379,11 @@ function isLoopingLogicValid(data, publishType) {
if (
(data?.cycle_pages || []).every((i) => {
return (
i.question_index &&
i.relation_type !== undefined &&
i.relation_type !== null &&
i.first_page &&
i.last_page
i.question_index
&& i.relation_type !== undefined
&& i.relation_type !== null
&& i.first_page
&& i.last_page
);
})
) {
@@ -397,7 +407,7 @@ function isLoopingLogicValid(data, publishType) {
* @param sn
* @param publishType undefined投放null投放0投放1预览2投放3测试
*/
export const canPlanetPublish = async function (sn, publishType) {
export const canPlanetPublish = async function(sn, publishType) {
const parsedPublishType = !publishType ? 2 : publishType;
const num = window.location.href.indexOf('code=');
let code;

View File

@@ -0,0 +1,56 @@
// const COS = require('cos-js-sdk-v5');
import COS from 'cos-js-sdk-v5';
/**
*
* @param {腾讯云的pramas} data
* @returns
*/
export default function createCOS(data) {
const {
bucket,
host,
region,
sessionToken,
tmpSecretId,
tmpSecretKey,
startTime,
expiredTime,
name,
file
} = data;
const config = {
Region: region,
Host: host,
Bucket: bucket,
Key: name,
Body: file,
onProgress: (progressData) => {
console.log(progressData);
}
};
const client = new COS({
getAuthorization: (options, callback) => {
// eslint-disable-next-line standard/no-callback-literal
callback({
TmpSecretId: tmpSecretId,
TmpSecretKey: tmpSecretKey,
SecurityToken: sessionToken,
// 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
StartTime: startTime, // 时间戳单位秒1580000000
ExpiredTime: expiredTime
});
}
// eslint-enable standard/no-callback-literal
});
return new Promise((resolve, reject) => {
client.putObject(config, (err, data) => {
console.log('腾讯上传', err, data);
if (err) {
reject(err);
} else {
resolve(data.Location);
}
});
});
}

View File

@@ -57,9 +57,11 @@
</template>
</option-action>
</van-radio-group>
<van-checkbox-group v-if="element.question_type === 2" v-model="value" shape="square">
<option-action
v-model:data="element.options[optionIndex]"
handle=".moverQues"
:active="active"
:question="element"
>

View File

@@ -13,7 +13,6 @@ const signatureCanvas = useTemplateRef('signatureCanvas');
const canvasWidth = ref(100);
const canvasHeight = computed(() => canvasWidth.value / 1.5);
const showSignText = ref(true);
const isEraser = ref(false);
let ctx: CanvasRenderingContext2D;
@@ -193,7 +192,7 @@ const emitValue = () => {
style="border: 1px dashed #ccc; border-radius: 4px"
>
</canvas>
<div class="sign-text" v-if="active" :class="{ show: active }">
<div v-if="active" class="sign-text" :class="{ show: active }">
<span
class="icon mobilefont mobilefont-qingkong"
title="清空"
@@ -207,7 +206,7 @@ const emitValue = () => {
></span>
<span class="icon mobilefont mobilefont-shangchuan" @click="saveCanvas"></span>
</div>
<div class="sign-tips" v-else>请在空白区域书写您的签名</div>
<div v-else class="sign-tips">请在空白区域书写您的签名</div>
</div>
</template>
</van-field>
@@ -259,13 +258,14 @@ const emitValue = () => {
}
}
}
.sign-tips {
position: absolute;
left: 50%;
top: 50%;
left: 50%;
width: max-content;
transform: translate(-50%, -50%);
color: #cdcdcd;
transform: translate(-50%, -50%);
}
}
</style>

View File

@@ -97,6 +97,10 @@
<!-- 底部功能性按钮 -->
<div class="survey-action">
<!-- <div class="survey-action_setting" @click="openSettingAction">-->
<!-- <van-icon name="setting" size="18" class="grid-icon" />-->
<!-- <span>投放设置</span>-->
<!-- </div>-->
<div class="survey-action_btn">
<van-button size="small" plain type="primary" @click="saveAs">保存</van-button>
<van-button size="small" plain type="primary" @click="previewQuestion">预览</van-button>
@@ -397,14 +401,14 @@ const questionEvent = (item) => {
options:
item.json.options.length > 0
? item.json.options.map((item) => {
return item.map((it) => {
return {
...it,
// 主键生成
id: uuidv4()
};
});
})
return item.map((it) => {
return {
...it,
// 主键生成
id: uuidv4()
};
});
})
: []
})
);
@@ -537,7 +541,7 @@ const previewQuestion = () => {
router.push({ name: 'preview', query: { ...route.query } });
};
onMounted(async () => {
onMounted(async() => {
await getQuestionDetail();
});
</script>

View File

@@ -4,9 +4,11 @@
<div class="content">
<van-cell-group v-if="status === 1" inset style="padding-top: 15px">
<div>
<img width="100%"
<img
width="100%"
src="https://files.axshare.com/gsc/DR6075/44/1a/03/441a03a8b1004755a7a392b311acf97f/images/%E6%8A%95%E6%94%BE/u14.jpg?pageId=2f9ba10c-92b8-4c9b-b40b-04e65a0b4333"
alt="" />
alt=""
/>
</div>
<div class="qrcode">
<img :src="publishInfo?.img_url" alt="" width="100px" height="100px" />
@@ -26,8 +28,13 @@
<div v-if="status === 0 || status === 2" class="pulish-container">
<img class="not-publish-icon" src="@/assets/img/publish/not_pulish.png" alt="" />
<div class="text">点击"启用"按钮后问卷才可以开始回收数据</div>
<van-button type="primary" style="margin-top: 20px" class="publish-btn" color="#70b936"
@click="openPublishModal">
<van-button
type="primary"
style="margin-top: 20px"
class="publish-btn"
color="#70b936"
@click="openPublishModal"
>
<template #icon>
<i class="mobilefont icon-fabu3" style="margin-right: 6px"></i>
</template>
@@ -92,21 +99,21 @@ type OperateItem = (typeof operateList)[0];
const operateBtn = (item: OperateItem) => {
switch (item.type) {
case 'shareLink':
shareLink();
break;
case 'copyLink':
copyLink();
break;
case 'qrCode':
downLoadImg();
break;
default:
break;
case 'shareLink':
shareLink();
break;
case 'copyLink':
copyLink();
break;
case 'qrCode':
downLoadImg();
break;
default:
break;
}
};
// 复制链接
function copyLink () {
function copyLink() {
const input = document.createElement('input');
input.value = publishInfo.value.url;
document.body.appendChild(input);
@@ -116,7 +123,7 @@ function copyLink () {
showToast('复制成功');
}
// 分享链接
function shareLink () {
function shareLink() {
const params = {
type: 'shareToWx',
title: publishInfo.value.download_url.title,
@@ -126,11 +133,11 @@ function shareLink () {
scene: 0 // 朋友圈1 微信好友0
};
console.log('shareUrl', publishInfo.value.url);
appBridge.shareToWeChat(params, () => { });
appBridge.shareToWeChat(params);
}
// 下载二维码
function downLoadImg () {
function downLoadImg() {
const { title, url } = publishInfo.value.download_url;
if (utils.getSessionStorage('xToken')) {
appBridge.save2Album(url, () => {
@@ -145,7 +152,7 @@ function downLoadImg () {
document.body.removeChild(link);
}
}
async function openPublishModal () {
async function openPublishModal() {
const res = await canPlanetPublish(route.query.sn as string, publishType.value);
if (res) {
await publishSurvey({
@@ -161,7 +168,7 @@ async function openPublishModal () {
}
}
function getCode () {
function getCode() {
getQrcode(sn)
.then((res) => {
if (res.data) {
@@ -172,7 +179,7 @@ function getCode () {
showFailToast(error.data?.message || error.message || '服务器错误');
});
}
function fetchInfo () {
function fetchInfo() {
getSurveyInfo(sn)
.then((res) => {
status.value = Number(res.data.data.status);
@@ -186,7 +193,7 @@ watch(status, (val) => {
getCode();
}
});
onMounted(async () => {
onMounted(async() => {
// status.value = 0;
// publishInfo.value.img_url
// = 'https://test-cxp-pubcos.yili.com/uat-yls//survey-api/publish/202503130938138261340.png';