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 // @ts-nocheck
// Generated by unplugin-vue-components // Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399 // Read more: https://github.com/vuejs/core/pull/3399
export {}; export {}
/* prettier-ignore */ /* prettier-ignore */
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
Contenteditable: typeof import('./src/components/contenteditable.vue')['default'] 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'] ElInput: typeof import('element-plus/es')['ElInput']
ElOption: typeof import('element-plus/es')['ElOption'] ElOption: typeof import('element-plus/es')['ElOption']
ElSelect: typeof import('element-plus/es')['ElSelect'] 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'] RichText: typeof import('./src/components/RichText.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
@@ -32,8 +38,13 @@ declare module 'vue' {
VanRadio: typeof import('vant/es')['Radio'] VanRadio: typeof import('vant/es')['Radio']
VanRadioGroup: typeof import('vant/es')['RadioGroup'] VanRadioGroup: typeof import('vant/es')['RadioGroup']
VanRow: typeof import('vant/es')['Row'] VanRow: typeof import('vant/es')['Row']
VanSearch: typeof import('vant/es')['Search']
VanStepper: typeof import('vant/es')['Stepper'] VanStepper: typeof import('vant/es')['Stepper']
VanSwitch: typeof import('vant/es')['Switch'] 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'] YLCascader: typeof import('./src/components/YLCascader.vue')['default']
YLInput: typeof import('./src/components/YLInput.vue')['default'] YLInput: typeof import('./src/components/YLInput.vue')['default']
YLPicker: typeof import('./src/components/YLPicker.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> <template>
<div class="flex contenteditable align-center space-between" :class="className"> <div class="flex contenteditable align-center space-between" :class="className">
<p <p
:id="'editor' + id"
ref="editor" ref="editor"
:contenteditable="active" :contenteditable="active"
class="van-field contenteditable-content" class="van-field contenteditable-content"
@focus="onFocus"
v-html="modelValue" v-html="modelValue"
></p> ></p>
<div class="right-icon ml10"> <div class="right-icon ml10">
@@ -13,13 +15,15 @@
<div v-if="showAction && active" ref="editorAction" class="editor-action"> <div v-if="showAction && active" ref="editorAction" class="editor-action">
<button v-for="item in actions" :key="item.name" @click="funEvent(item, $event)"> <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> </button>
</div> </div>
</template> </template>
<script setup> <script setup>
import { defineEmits, ref, onMounted, watch } from 'vue'; import { defineEmits, ref, onMounted, watch } from 'vue';
import { v4 as uuidv4 } from 'uuid';
import CommonApi from '@/api/common.js';
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@@ -36,6 +40,8 @@ const props = defineProps({
} }
}); });
const id = ref(uuidv4());
const editor = ref(null); const editor = ref(null);
const editorAction = ref(null); const editorAction = ref(null);
const emit = defineEmits(['update:modelValue', 'blur']); const emit = defineEmits(['update:modelValue', 'blur']);
@@ -43,8 +49,9 @@ const save = (e) => {
emit('update:modelValue', e.innerHTML); emit('update:modelValue', e.innerHTML);
}; };
const savedRange = ref(null);
const functions = { const functions = {
// todo 点击按钮之后 如何判断 才能让按钮再次点击 不消失 获取重新聚焦 再次选中文本?
boldModern: () => { boldModern: () => {
document.execCommand('bold', true, null); document.execCommand('bold', true, null);
}, },
@@ -53,13 +60,45 @@ const functions = {
}, },
italic: () => { italic: () => {
document.execCommand('italic', false, null); 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 funEvent = (item) => {
// 保持焦点在编辑器
const selection = window.getSelection();
selection.getRangeAt(0);
functions[item.fun](); functions[item.fun]();
}; };
@@ -82,21 +121,22 @@ const actions = [
{ {
label: '加粗', label: '加粗',
fun: 'boldModern', fun: 'boldModern',
icon: '' icon: 'jiacu'
}, },
{ {
label: '下划线', label: '下划线',
fun: 'underLine', fun: 'underLine',
icon: '' icon: 'xiahuaxian'
}, },
{ {
label: '图片上传', label: '图片上传',
fun: 'uploadImage', fun: 'uploadImage',
icon: '' icon: 'tupian'
}, },
{ {
label: '倾斜', label: '倾斜',
fun: 'italic' fun: 'italic',
icon: 'qingxie'
} }
]; ];
const checkContains = (element, target) => { const checkContains = (element, target) => {
@@ -112,8 +152,6 @@ onMounted(() => {
showAction.value = true; showAction.value = true;
}); });
// 如果点击了 editor 与 editorAction 其他地方就触发保存
document.addEventListener('click', (e) => { document.addEventListener('click', (e) => {
if (!editor.value || !editorAction.value) return; if (!editor.value || !editorAction.value) return;
@@ -127,19 +165,58 @@ onMounted(() => {
emit('blur', editor.value); emit('blur', editor.value);
} }
}); });
// editor.value.addEventListener('blur', () => {
// setTimeout(() => {
// showAction.value = false;
// });
// });
document.addEventListener('resize', () => { document.addEventListener('resize', () => {
showAction.value = false; showAction.value = false;
}); });
}); });
// onBeforeUnmount(() => { const onFocus = (e) => {
// editor.value.removeEventListener('resize', handleResize); // 阻止
// }); 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> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -166,7 +243,12 @@ onMounted(() => {
& button { & button {
border: none; border: none;
background: #fff; background: #fff;
color: #000;
outline: none; outline: none;
} }
button + button {
margin-left: 10px;
}
} }
</style> </style>

View File

@@ -14,6 +14,22 @@
-moz-osx-font-smoothing: grayscale; -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 { .mobilefont-del1::before {
content: '\e637'; content: '\e637';
} }

Binary file not shown.

View File

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

View File

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

View File

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

View File

@@ -97,6 +97,10 @@
<!-- 底部功能性按钮 --> <!-- 底部功能性按钮 -->
<div class="survey-action"> <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"> <div class="survey-action_btn">
<van-button size="small" plain type="primary" @click="saveAs">保存</van-button> <van-button size="small" plain type="primary" @click="saveAs">保存</van-button>
<van-button size="small" plain type="primary" @click="previewQuestion">预览</van-button> <van-button size="small" plain type="primary" @click="previewQuestion">预览</van-button>
@@ -537,7 +541,7 @@ const previewQuestion = () => {
router.push({ name: 'preview', query: { ...route.query } }); router.push({ name: 'preview', query: { ...route.query } });
}; };
onMounted(async () => { onMounted(async() => {
await getQuestionDetail(); await getQuestionDetail();
}); });
</script> </script>

View File

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