洞察报告;

This commit is contained in:
钱冠学
2024-09-11 14:39:42 +08:00
parent 5963c4f682
commit 31814a5993
23 changed files with 467 additions and 37 deletions

View File

@@ -2,6 +2,7 @@
<div id="tinys" class="tiny" :class="{ 'tiny-curtail': curtail, 'popup-menu-align-right': popupMenuAlignRight }"> <div id="tinys" class="tiny" :class="{ 'tiny-curtail': curtail, 'popup-menu-align-right': popupMenuAlignRight }">
<Editor <Editor
v-model="content" v-model="content"
:disabled="disabled"
:style="{ minHeight: `${curtailMinHeight}px` }" :style="{ minHeight: `${curtailMinHeight}px` }"
:init="init" :init="init"
style="overflow-wrap: break-word; word-break: break-word;" style="overflow-wrap: break-word; word-break: break-word;"
@@ -408,6 +409,10 @@ export default {
type: String, type: String,
default: "请输入", default: "请输入",
}, },
disabled: {
type: Boolean,
default: false,
},
defaultFontSize: { defaultFontSize: {
type: Number, type: Number,
default: 14, default: 14,
@@ -668,6 +673,7 @@ function initTinymce(data) {
skin_url: `${baseOss}/tinymce/skins/ui/oxide`, skin_url: `${baseOss}/tinymce/skins/ui/oxide`,
menubar: false, // 隐藏菜单栏 menubar: false, // 隐藏菜单栏
statusbar: false, statusbar: false,
placeholder: props.placeholder,
fixed_toolbar_container: "#tinys", fixed_toolbar_container: "#tinys",
inline: true, // 开启内联模式 inline: true, // 开启内联模式
auto_focus: true, auto_focus: true,
@@ -766,6 +772,7 @@ function initTinymce(data) {
skin_url: `${baseOss}/tinymce/skins/ui/oxide`, skin_url: `${baseOss}/tinymce/skins/ui/oxide`,
menubar: false, // 隐藏菜单栏 menubar: false, // 隐藏菜单栏
statusbar: false, statusbar: false,
placeholder: props.placeholder,
height: 144, height: 144,
auto_focus: true, auto_focus: true,
max_height: 350, max_height: 350,
@@ -1378,4 +1385,10 @@ function util(store) {
:deep(.tox-collection__item-label){ :deep(.tox-collection__item-label){
white-space: nowrap; white-space: nowrap;
} }
:deep(.tox .tox-toolbar__overflow) {
margin-bottom: 0 !important;
}
:deep(.tox .tox-toolbar__primary) {
border-top: none !important;
}
</style> </style>

View File

@@ -54,6 +54,48 @@
<div class="content unicode" style="display: block;"> <div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont">&#xe870;</span>
<div class="name">分享报告</div>
<div class="code-name">&amp;#xe870;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe873;</span>
<div class="name">更新报告</div>
<div class="code-name">&amp;#xe873;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe879;</span>
<div class="name">洞察报告</div>
<div class="code-name">&amp;#xe879;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe86f;</span>
<div class="name">icon_BPTO</div>
<div class="code-name">&amp;#xe86f;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe86e;</span>
<div class="name">icon_recording</div>
<div class="code-name">&amp;#xe86e;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe86d;</span>
<div class="name">icon_Export Password</div>
<div class="code-name">&amp;#xe86d;</div>
</li>
<li class="dib">
<span class="icon iconfont">&#xe86b;</span>
<div class="name">icon_autogenerate</div>
<div class="code-name">&amp;#xe86b;</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont">&#xe86c;</span> <span class="icon iconfont">&#xe86c;</span>
<div class="name">icon_timer</div> <div class="name">icon_timer</div>
@@ -3510,9 +3552,9 @@
<pre><code class="language-css" <pre><code class="language-css"
>@font-face { >@font-face {
font-family: 'iconfont'; font-family: 'iconfont';
src: url('iconfont.woff2?t=1698731786246') format('woff2'), src: url('iconfont.woff2?t=1726032533651') format('woff2'),
url('iconfont.woff?t=1698731786246') format('woff'), url('iconfont.woff?t=1726032533651') format('woff'),
url('iconfont.ttf?t=1698731786246') format('truetype'); url('iconfont.ttf?t=1726032533651') format('truetype');
} }
</code></pre> </code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3> <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -3538,6 +3580,69 @@
<div class="content font-class"> <div class="content font-class">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<span class="icon iconfont icon-fenxiangbaogao"></span>
<div class="name">
分享报告
</div>
<div class="code-name">.icon-fenxiangbaogao
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-gengxinbaogao"></span>
<div class="name">
更新报告
</div>
<div class="code-name">.icon-gengxinbaogao
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-shujumingxi2"></span>
<div class="name">
洞察报告
</div>
<div class="code-name">.icon-shujumingxi2
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-icon_BPTO"></span>
<div class="name">
icon_BPTO
</div>
<div class="code-name">.icon-icon_BPTO
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-icon_recording"></span>
<div class="name">
icon_recording
</div>
<div class="code-name">.icon-icon_recording
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-a-icon_ExportPassword"></span>
<div class="name">
icon_Export Password
</div>
<div class="code-name">.icon-a-icon_ExportPassword
</div>
</li>
<li class="dib">
<span class="icon iconfont icon-icon_autogenerate"></span>
<div class="name">
icon_autogenerate
</div>
<div class="code-name">.icon-icon_autogenerate
</div>
</li>
<li class="dib"> <li class="dib">
<span class="icon iconfont icon-icon_timer"></span> <span class="icon iconfont icon-icon_timer"></span>
<div class="name"> <div class="name">
@@ -8722,6 +8827,62 @@
<div class="content symbol"> <div class="content symbol">
<ul class="icon_lists dib-box"> <ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-fenxiangbaogao"></use>
</svg>
<div class="name">分享报告</div>
<div class="code-name">#icon-fenxiangbaogao</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-gengxinbaogao"></use>
</svg>
<div class="name">更新报告</div>
<div class="code-name">#icon-gengxinbaogao</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-shujumingxi2"></use>
</svg>
<div class="name">洞察报告</div>
<div class="code-name">#icon-shujumingxi2</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-icon_BPTO"></use>
</svg>
<div class="name">icon_BPTO</div>
<div class="code-name">#icon-icon_BPTO</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-icon_recording"></use>
</svg>
<div class="name">icon_recording</div>
<div class="code-name">#icon-icon_recording</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-a-icon_ExportPassword"></use>
</svg>
<div class="name">icon_Export Password</div>
<div class="code-name">#icon-a-icon_ExportPassword</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-icon_autogenerate"></use>
</svg>
<div class="name">icon_autogenerate</div>
<div class="code-name">#icon-icon_autogenerate</div>
</li>
<li class="dib"> <li class="dib">
<svg class="icon svg-icon" aria-hidden="true"> <svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#icon-icon_timer"></use> <use xlink:href="#icon-icon_timer"></use>

View File

@@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 3121635 */ font-family: "iconfont"; /* Project id 3121635 */
src: url('iconfont.woff2?t=1698731786246') format('woff2'), src: url('iconfont.woff2?t=1726032533651') format('woff2'),
url('iconfont.woff?t=1698731786246') format('woff'), url('iconfont.woff?t=1726032533651') format('woff'),
url('iconfont.ttf?t=1698731786246') format('truetype'); url('iconfont.ttf?t=1726032533651') format('truetype');
} }
.iconfont { .iconfont {
@@ -13,6 +13,34 @@
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-fenxiangbaogao:before {
content: "\e870";
}
.icon-gengxinbaogao:before {
content: "\e873";
}
.icon-shujumingxi2:before {
content: "\e879";
}
.icon-icon_BPTO:before {
content: "\e86f";
}
.icon-icon_recording:before {
content: "\e86e";
}
.icon-a-icon_ExportPassword:before {
content: "\e86d";
}
.icon-icon_autogenerate:before {
content: "\e86b";
}
.icon-icon_timer:before { .icon-icon_timer:before {
content: "\e86c"; content: "\e86c";
} }

File diff suppressed because one or more lines are too long

View File

@@ -5,6 +5,55 @@
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "调研猩球前台icon", "description": "调研猩球前台icon",
"glyphs": [ "glyphs": [
{
"icon_id": "41476175",
"name": "分享报告",
"font_class": "fenxiangbaogao",
"unicode": "e870",
"unicode_decimal": 59504
},
{
"icon_id": "41476179",
"name": "更新报告",
"font_class": "gengxinbaogao",
"unicode": "e873",
"unicode_decimal": 59507
},
{
"icon_id": "41812315",
"name": "洞察报告",
"font_class": "shujumingxi2",
"unicode": "e879",
"unicode_decimal": 59513
},
{
"icon_id": "38184565",
"name": "icon_BPTO",
"font_class": "icon_BPTO",
"unicode": "e86f",
"unicode_decimal": 59503
},
{
"icon_id": "38180243",
"name": "icon_recording",
"font_class": "icon_recording",
"unicode": "e86e",
"unicode_decimal": 59502
},
{
"icon_id": "37956036",
"name": "icon_Export Password",
"font_class": "a-icon_ExportPassword",
"unicode": "e86d",
"unicode_decimal": 59501
},
{
"icon_id": "37956037",
"name": "icon_autogenerate",
"font_class": "icon_autogenerate",
"unicode": "e86b",
"unicode_decimal": 59499
},
{ {
"icon_id": "37621370", "icon_id": "37621370",
"name": "icon_timer", "name": "icon_timer",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -217,7 +217,7 @@ export default defineComponent({
if(checkShowInsightTab({ templateType: store.state.common.surveyInfo.template_type })) { if(checkShowInsightTab({ templateType: store.state.common.surveyInfo.template_type })) {
menus.value.unshift({ menus.value.unshift({
name: '洞察报告', name: '洞察报告',
icon: '&#xe696;', icon: '&#xe879;',
children: [], children: [],
path: '/survey/analyse/insight' path: '/survey/analyse/insight'
}) })

View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref } from 'vue' import { onBeforeUnmount, ref } from 'vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { Modal } from 'ant-design-vue' import { Modal } from 'ant-design-vue'
@@ -10,13 +10,17 @@ import InsightShare from './components/InsightShare.vue'
import Report from './report/Report.vue' import Report from './report/Report.vue'
import { editInsightReport, getInsightReport, updateInsightReport } from './api' import { editInsightReport, getInsightReport, checkReportStatus, updateInsightReport } from './api'
const route = useRoute() const route = useRoute()
const sn = route.query.sn || '' const sn = route.query.sn || ''
const initialized = ref(false)
const loading = ref(false) const loading = ref(false)
const updating = ref(false) // 更新报告
let timer = 1
const report = ref({}) const report = ref({})
const saving = ref(0) const saving = ref(0)
@@ -38,6 +42,11 @@ async function getReport() {
initSaveParams() initSaveParams()
loading.value = false loading.value = false
if(!initialized.value) {
startLooping()
}
initialized.value = true
} }
function initSaveParams() { function initSaveParams() {
@@ -87,17 +96,14 @@ function onGenerateReport(isInit) {
} }
async function onConfirmGenerateReport() { async function onConfirmGenerateReport() {
if(loading.value) { if(updating.value) {
return return
} }
loading.value = true updating.value = true
const params = { surveySn: sn } const params = { surveySn: sn }
await updateInsightReport(params).catch(() => '') await updateInsightReport(params).catch(() => '')
await getReport()
loading.value = false
getReport()
} }
@@ -127,17 +133,73 @@ function mergeReport(params) {
Object.keys(params.actions).forEach((key) => result[key] = params.actions[key]) Object.keys(params.actions).forEach((key) => result[key] = params.actions[key])
} }
} }
function startLooping() {
if(!timer) {
return
}
timer = setTimeout(() => {
getReportStatus()
}, 3e3)
}
function stopLooping() {
clearTimeout(timer)
timer = null
}
onBeforeUnmount(stopLooping)
async function getReportStatus() {
const params = {
sn,
reportVersion: report.value.reportVersionReal,
likeModelWordCloudStatus: report.value.likeModelWordCloudStatus,
hateModelWordCloudStatus: report.value.hateModelWordCloudStatus
}
const data = await checkReportStatus(params).catch(() => '')
// 0=默认,不需要处理
// 1=有新的报告产生,且未完成,不可以进行更新报告,需要置灰
// 2=有新的报告产生,已完成,需要重新请求报告展示接口
// 3=老报告的词云更新完了,需要重新请求报告展示接口
switch(data?.data?.code) {
case 0:
updating.value = false
break
case 1:
updating.value = true
break
case 2:
case 3:
await getReport()
break
default:
}
startLooping()
}
</script> </script>
<template> <template>
<a-spin v-if="loading" :spinning="loading" tip="加载中" class="spinning" /> <a-spin v-if="!initialized"
:spinning="true"
tip="加载中"
class="spinning" />
<div v-else class="insight-page"> <div v-else class="insight-page">
<InsightEmpty v-if="!report?.id" @generate="onGenerateReport" /> <InsightEmpty v-if="!report?.id" @generate="onGenerateReport" />
<template v-if="report?.id"> <template v-if="report?.id">
<InsightShare :report="report" :saving="saving" @regenerate="onGenerateReport" /> <InsightShare :report="report"
:saving="saving"
:updating="updating"
@regenerate="onGenerateReport" />
<Report :report="report" @change="editReport" /> <Report :report="report" :updating="updating" @change="editReport" />
</template> </template>
</div> </div>
</template> </template>

View File

@@ -29,6 +29,20 @@ export function getInsightReport(params) {
} }
/**
* 检查状态
* @param data
* @returns {*}
*/
export function checkReportStatus(data) {
return request({
url: `/console/insightReport/${ data.sn }/status`,
method: 'post',
data
})
}
/** /**
* 修改洞察报告 * 修改洞察报告
* @param data * @param data

View File

@@ -1,5 +1,5 @@
<script setup> <script setup>
import { defineEmits, defineProps, ref } from 'vue' import { defineEmits, defineProps, ref, watch } from 'vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import { InfoCircleOutlined } from '@ant-design/icons-vue' import { InfoCircleOutlined } from '@ant-design/icons-vue'
@@ -11,7 +11,8 @@ import useCopy from '@/composables/useCopy'
const emits = defineEmits(['regenerate']) const emits = defineEmits(['regenerate'])
const props = defineProps({ const props = defineProps({
report: { type: Object, default: () => Object.assign({}) }, report: { type: Object, default: () => Object.assign({}) },
saving: { type: Number, default: 0 } saving: { type: Number, default: 0 },
updating: { type: Boolean, default: false }
}) })
const loading = ref(false) const loading = ref(false)
@@ -104,7 +105,15 @@ async function onCopy() {
message.success('复制成功') message.success('复制成功')
} }
const updating = ref(false)
watch(() => props.updating, (val) => updating.value = !!val, { immediate: true })
function onUpdateReport() { function onUpdateReport() {
if(updating.value) {
message.info('问卷更新中')
return
}
emits('regenerate') emits('regenerate')
} }
</script> </script>
@@ -162,12 +171,14 @@ function onUpdateReport() {
</a-tooltip> </a-tooltip>
<a-button class="custom-button button mr-20" @click.stop="onShowShare"> <a-button class="custom-button button mr-20" @click.stop="onShowShare">
<img src="../img/icon_share.png" alt="" class="icon"> <span class="iconfont icon-fenxiangbaogao" />
<span>分享报告</span> <span>分享报告</span>
</a-button> </a-button>
<a-button class="custom-button button mr-10" @click="onUpdateReport"> <a-button class="custom-button button mr-10"
<img src="../img/icon_refresh.png" alt="" class="icon"> :class="{'updating': updating}"
@click="onUpdateReport">
<span class="iconfont icon-gengxinbaogao" />
<span>更新报告</span> <span>更新报告</span>
</a-button> </a-button>
@@ -218,6 +229,42 @@ function onUpdateReport() {
height: 16px; height: 16px;
margin-right: 4px; margin-right: 4px;
border: none; border: none;
animation: all 0.3s;
}
.iconfont {
display: block;
height: 1em;
line-height: 1em;
font-size: 16px;
margin-right: 4px;
animation: all 0.3s;
}
&.updating {
cursor: not-allowed;
.iconfont {
animation-name: icon-rotation;
animation-duration: 1.2s;
animation-direction: normal;
animation-iteration-count: infinite;
animation-timing-function: linear;
}
@keyframes icon-rotation {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(180deg);
}
100% {
transform: rotate(360deg);
}
}
} }
} }

View File

@@ -11,3 +11,5 @@ export function checkShowInsightTab({ templateType } = {}) {
return templateType && showInsightTemplateType.includes(templateType) return templateType && showInsightTemplateType.includes(templateType)
} }
export const reportUpdatingMessageText = '报告更新中,不能修改'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 706 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 455 B

View File

@@ -14,11 +14,13 @@ import ConceptDiagnosis from './section/conceptDiagnosis/ConceptDiagnosis.vue'
const emits = defineEmits(['change']) const emits = defineEmits(['change'])
const props = defineProps({ const props = defineProps({
report: { type: Object, default: () => Object.assign({}) }, report: { type: Object, default: () => Object.assign({}) },
readonly: { type: Boolean, default: false } readonly: { type: Boolean, default: false },
updating: { type: Boolean, default: false } // 报告更新中
}) })
const report = computed(() => props.report || {}) const report = computed(() => props.report || {})
const readonly = computed(() => props.readonly || false) const readonly = computed(() => props.readonly || false)
const updating = computed(() => props.updating || false)
// 快测版报告内容和标准版基本一致,区别为快测版没有概念诊断部分 // 快测版报告内容和标准版基本一致,区别为快测版没有概念诊断部分
// 看板类型1=标准版,2=快测版,3=配对版 // 看板类型1=标准版,2=快测版,3=配对版
@@ -56,6 +58,7 @@ function onChange(evt) {
:is="com" :is="com"
:report="report" :report="report"
:readonly="readonly" :readonly="readonly"
:updating="updating"
@change="onChange" /> @change="onChange" />
</div> </div>
</template> </template>

View File

@@ -8,6 +8,7 @@ const props = defineProps({
barTable: { type: Boolean, default: false }, barTable: { type: Boolean, default: false },
selectionRowTitle: { type: Array, default: () => ['是否通过概念行动标准'] }, // 表格第一列的值为这几个值的时候,这一样其余列要显示“是”“否”选择框 selectionRowTitle: { type: Array, default: () => ['是否通过概念行动标准'] }, // 表格第一列的值为这几个值的时候,这一样其余列要显示“是”“否”选择框
readonly: { type: Boolean, default: false }, readonly: { type: Boolean, default: false },
updating: { type: Boolean, default: false }, // 报告更新中
rowTitleColumnWidth: { type: Number, default: 280 }, // 表格行头部的宽度 rowTitleColumnWidth: { type: Number, default: 280 }, // 表格行头部的宽度
rowSecondTitleColumnWidth: { type: Number, default: 280 }, // 表格行头部第二列的宽度 rowSecondTitleColumnWidth: { type: Number, default: 280 }, // 表格行头部第二列的宽度
@@ -294,6 +295,7 @@ function updateSelectionChange(record) {
</template> </template>
<a-select v-else <a-select v-else
v-model:value="record[column.dataIndex]" v-model:value="record[column.dataIndex]"
:disabled="props.updating"
placeholder="请选择" placeholder="请选择"
class="custom-select" class="custom-select"
style="width: 90px;" style="width: 90px;"

View File

@@ -66,8 +66,6 @@ function initWordCloud() {
} }
chart.setOption(option) chart.setOption(option)
console.log('====', option)
chart.on('click', (param) => { chart.on('click', (param) => {
emits('change', param.data) emits('change', param.data)
}) })

View File

@@ -11,7 +11,8 @@ import Section from '../../components/Section.vue'
const emits = defineEmits(['change']) const emits = defineEmits(['change'])
const props = defineProps({ const props = defineProps({
report: { type: Object, default: () => Object.assign({}) }, report: { type: Object, default: () => Object.assign({}) },
readonly: { type: Boolean, default: false } readonly: { type: Boolean, default: false },
updating: { type: Boolean, default: false } // 报告更新中
}) })
@@ -36,6 +37,7 @@ function onBlur() {
</Section> </Section>
<Section v-else class="section"> <Section v-else class="section">
<Tinymce v-model:editorData="richText" <Tinymce v-model:editorData="richText"
:disabled="props.updating"
:curtail="false" :curtail="false"
:curtail-min-height="140" :curtail-min-height="140"
show show
@@ -57,6 +59,7 @@ function onBlur() {
:deep(.tiny) { :deep(.tiny) {
height: 100%; height: 100%;
padding-top: 0; padding-top: 0;
background: unset;
.tox-tinymce { .tox-tinymce {
overflow: hidden; overflow: hidden;

View File

@@ -1,5 +1,6 @@
<script setup> <script setup>
import { defineEmits, defineProps, ref, watch } from 'vue' import { defineEmits, defineProps, ref, watch } from 'vue'
import { message } from 'ant-design-vue'
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons-vue' import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons-vue'
@@ -7,11 +8,14 @@ import SectionTitle from '../../components/SectionTitle.vue'
import Section from '../../components/Section.vue' import Section from '../../components/Section.vue'
import StyledTable from '../components/StyledTable.vue' import StyledTable from '../components/StyledTable.vue'
import { reportUpdatingMessageText } from '../../consts'
const emits = defineEmits(['change']) const emits = defineEmits(['change'])
const props = defineProps({ const props = defineProps({
report: { type: Object, default: () => Object.assign({}) }, report: { type: Object, default: () => Object.assign({}) },
readonly: { type: Boolean, default: false } readonly: { type: Boolean, default: false },
updating: { type: Boolean, default: false } // 报告更新中
}) })
@@ -102,12 +106,22 @@ function getTableCodeRow(tableData) {
} }
function toggleVisible(item) { function toggleVisible(item) {
if(props.updating) {
message.warning(reportUpdatingMessageText)
return
}
item.visible = !item.visible item.visible = !item.visible
emits('change', { [item.visibleField]: item.visible ? 1 : 2 }) emits('change', { [item.visibleField]: item.visible ? 1 : 2 })
} }
function onChange(evt) { function onChange(evt) {
if(props.updating) {
message.warning(reportUpdatingMessageText)
return
}
emits('change', evt) emits('change', evt)
} }
</script> </script>
@@ -121,6 +135,7 @@ function onChange(evt) {
:row-title-column-width="item.rowTitleColumnWidth" :row-title-column-width="item.rowTitleColumnWidth"
:row-second-title-column-width="item.rowSecondTitleColumnWidth" :row-second-title-column-width="item.rowSecondTitleColumnWidth"
:readonly="props.readonly" :readonly="props.readonly"
:updating="props.updating"
@change="onChange" /> @change="onChange" />
<div class="message"> <div class="message">
@@ -135,8 +150,14 @@ function onChange(evt) {
<template #tab> <template #tab>
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<template v-if="item.canHide && !props.readonly"> <template v-if="item.canHide && !props.readonly">
<EyeOutlined v-if="item.visible" class="icon" @click.stop="toggleVisible(item)" /> <EyeOutlined v-if="item.visible"
<EyeInvisibleOutlined v-else class="icon" @click.stop="toggleVisible(item)" /> class="icon"
:class="{'disabled': props.updating}"
@click.stop="toggleVisible(item)" />
<EyeInvisibleOutlined v-else
class="icon"
:class="{'disabled': props.updating}"
@click.stop="toggleVisible(item)" />
</template> </template>
</template> </template>
@@ -145,6 +166,7 @@ function onChange(evt) {
:row-title-column-width="item.rowTitleColumnWidth" :row-title-column-width="item.rowTitleColumnWidth"
:row-second-title-column-width="item.rowSecondTitleColumnWidth" :row-second-title-column-width="item.rowSecondTitleColumnWidth"
:readonly="props.readonly" :readonly="props.readonly"
:updating="props.updating"
@change="onChange" /> @change="onChange" />
<div class="message"> <div class="message">
@@ -182,6 +204,10 @@ function onChange(evt) {
margin-left: 6px; margin-left: 6px;
font-size: 14px; font-size: 14px;
color: #B9B9B9; color: #B9B9B9;
&.disabled {
cursor: not-allowed;
}
} }
.message { .message {

View File

@@ -1,5 +1,6 @@
<script setup> <script setup>
import { computed, defineEmits, defineProps, ref } from 'vue' import { computed, defineEmits, defineProps, ref } from 'vue'
import { message } from 'ant-design-vue'
import moment from 'moment' import moment from 'moment'
import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons-vue' import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons-vue'
@@ -8,11 +9,14 @@ import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons-vue'
import SectionTitle from '../../components/SectionTitle.vue' import SectionTitle from '../../components/SectionTitle.vue'
import Section from '../../components/Section.vue' import Section from '../../components/Section.vue'
import { reportUpdatingMessageText } from '../../consts'
const emits = defineEmits(['change']) const emits = defineEmits(['change'])
const props = defineProps({ const props = defineProps({
report: { type: Object, default: () => Object.assign({}) }, report: { type: Object, default: () => Object.assign({}) },
readonly: { type: Boolean, default: false } readonly: { type: Boolean, default: false },
updating: { type: Boolean, default: false } // 报告更新中
}) })
const createdAt = computed(() => props.report?.createdAt ? moment(props.report.createdAt).format('YYYY年MM月DD日 HH:mm:ss') : '--') const createdAt = computed(() => props.report?.createdAt ? moment(props.report.createdAt).format('YYYY年MM月DD日 HH:mm:ss') : '--')
@@ -27,6 +31,11 @@ const reportVersion = computed(() => props.report?.reportVersion ?? '--')
const visible = ref(+props.report.overviewHidden === 1) const visible = ref(+props.report.overviewHidden === 1)
function toggleVisibility() { function toggleVisibility() {
if(props.updating) {
message.warning(reportUpdatingMessageText)
return
}
visible.value = !visible.value visible.value = !visible.value
emits('change', { overviewHidden: visible.value ? 1 : 2 }) emits('change', { overviewHidden: visible.value ? 1 : 2 })
@@ -38,8 +47,14 @@ function toggleVisibility() {
<SectionTitle> <SectionTitle>
<span class="text">报告概览</span> <span class="text">报告概览</span>
<template v-if="!props.readonly"> <template v-if="!props.readonly">
<EyeOutlined v-if="visible" class="icon" @click="toggleVisibility" /> <EyeOutlined v-if="visible"
<EyeInvisibleOutlined v-else class="icon" @click.stop="toggleVisibility" /> class="icon"
:class="{'disabled': props.updating}"
@click="toggleVisibility" />
<EyeInvisibleOutlined v-else
class="icon"
:class="{'disabled': props.updating}"
@click.stop="toggleVisibility" />
</template> </template>
</SectionTitle> </SectionTitle>
@@ -85,6 +100,10 @@ function toggleVisibility() {
font-size: 14px; font-size: 14px;
color: #B9B9B9; color: #B9B9B9;
cursor: pointer; cursor: pointer;
&.disabled {
cursor: not-allowed;
}
} }
.section { .section {

View File

@@ -10,7 +10,8 @@ import Section from '../../components/Section.vue'
const emits = defineEmits(['change']) const emits = defineEmits(['change'])
const props = defineProps({ const props = defineProps({
report: { type: Object, default: () => Object.assign({}) }, report: { type: Object, default: () => Object.assign({}) },
readonly: { type: Boolean, default: false } readonly: { type: Boolean, default: false },
updating: { type: Boolean, default: false } // 报告更新中
}) })
@@ -43,6 +44,7 @@ function onBlur() {
</div> </div>
<div v-else class="value tinymce-wrapper"> <div v-else class="value tinymce-wrapper">
<Tinymce v-model:editorData="richText" <Tinymce v-model:editorData="richText"
:disabled="props.updating"
:curtail="false" :curtail="false"
:curtail-min-height="140" :curtail-min-height="140"
show show
@@ -84,6 +86,7 @@ function onBlur() {
:deep(.tiny) { :deep(.tiny) {
height: 100%; height: 100%;
padding-top: 0; padding-top: 0;
background: unset;
.tox-tinymce { .tox-tinymce {
overflow: hidden; overflow: hidden;

View File

@@ -90,11 +90,11 @@ watch(() => props.report, () => {
<a-tabs v-model:activeKey="activeKey" :animated="false"> <a-tabs v-model:activeKey="activeKey" :animated="false">
<a-tab-pane v-for="(item) in tabList" :key="item.key" :tab="item.name"> <a-tab-pane v-for="(item) in tabList" :key="item.key" :tab="item.name">
<div class="tab-container"> <div class="tab-container">
<PeopleDislike :type-str="item.name"
:data="item.children.find((child) => child.type.indexOf('不喜欢') > -1)" />
<PeopleLike title="消费者喜欢的方面" <PeopleLike title="消费者喜欢的方面"
:type-str="item.name" :type-str="item.name"
:data="item.children.find((child) => child.type.indexOf('喜欢') > -1 && child.type.indexOf('不喜欢') === -1)" /> :data="item.children.find((child) => child.type.indexOf('喜欢') > -1 && child.type.indexOf('不喜欢') === -1)" />
<PeopleDislike :type-str="item.name"
:data="item.children.find((child) => child.type.indexOf('不喜欢') > -1)" />
<ProductImage :report="props.report" /> <ProductImage :report="props.report" />
</div> </div>