Compare commits

34 Commits

Author SHA1 Message Date
DESKTOP-AD8UBUJ\ling
2eba5f0140 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_bi_web 2026-03-12 16:48:31 +08:00
DESKTOP-AD8UBUJ\ling
1dacb5a705 序号列宽固定 2026-03-12 16:46:18 +08:00
chy
99b2cc369d 修改单点登录 2026-03-11 00:16:42 +08:00
DESKTOP-AD8UBUJ\ling
0e48245381 Merge branch 'main' of http://8.130.49.250:3000/admin/gr_bi_web
# Conflicts:
#	config/dev.env.js
#	src/views/login.vue
2026-03-07 00:22:49 +08:00
DESKTOP-AD8UBUJ\ling
83a8974dc5 config提交 2026-03-07 00:20:09 +08:00
DESKTOP-AD8UBUJ\ling
76443e343c 单点修改2 2026-03-07 00:13:29 +08:00
chy
c5fdbd6a27 111111112 2026-03-06 22:45:55 +08:00
chy
ace17a95dd Merge branch 'main' of http://8.130.49.250:3000/admin/gr_bi_web
# Conflicts:
#	src/views/login.vue
2026-03-06 22:44:43 +08:00
chy
2125269c6c 1 2026-03-06 22:41:57 +08:00
DESKTOP-AD8UBUJ\ling
1373b56b94 单点修改1 2026-03-06 22:22:42 +08:00
DESKTOP-AD8UBUJ\ling
23f4390a9b 加阅读模式 2026-03-06 08:04:29 +08:00
DESKTOP-AD8UBUJ\ling
1f08afd8d3 下钻配置未清空上次配置问题修复 2026-03-05 15:27:35 +08:00
DESKTOP-AD8UBUJ\ling
c64012e4ea 补充 2026-03-02 21:35:40 +08:00
DESKTOP-AD8UBUJ\ling
cffdd0f7ba 大屏表格下钻 2026-03-02 21:34:56 +08:00
chy
1ba1186ee2 修改统一身份认证 2026-03-02 15:14:07 +08:00
chy
a869e62351 修改统一身份认证 2026-03-02 10:33:39 +08:00
chy
458ac46ca4 添加空字符 2026-02-28 08:46:34 +08:00
mll
8ec6582ffc bigscreen/viewer本页面路由跳转问题 2026-02-27 22:16:22 +08:00
mll
7110ba358c 大屏预览底部空白修复 2026-02-27 22:08:37 +08:00
b6fcf5c32b Merge pull request 'tab20260208' (#3) from tab20260208 into main
Reviewed-on: #3
2026-02-13 12:13:10 +08:00
Erin66
5cce73902d 下钻配置 tabs修改 2026-02-13 00:35:20 +08:00
Erin66
25db88333d Merge branch 'main' into tab20260208 2026-02-12 22:33:43 +08:00
Erin66
ddb542d89b tabs内部虚线区域放大 2026-02-12 22:30:38 +08:00
mll
01e9657d49 no message 2026-02-11 11:58:10 +08:00
01f15d8352 Merge pull request 'tabs组件优化' (#2) from tab20260208 into main
Reviewed-on: #2
2026-02-11 10:19:34 +08:00
Erin66
da7d02474f tabs组件优化 2026-02-11 00:01:20 +08:00
mll
9fbbd17e1f no message 2026-02-10 13:48:48 +08:00
mll
f1b3cb854f Merge branch 'main' of http://8.130.49.250:3000/admin/gr_bi_web 2026-02-10 13:39:22 +08:00
mll
5b39799d01 下钻弹窗最小高度100vh-34px 2026-02-10 13:39:19 +08:00
291ab91c99 Merge pull request 'tab20260208' (#1) from tab20260208 into main
Reviewed-on: #1
2026-02-10 09:11:13 +08:00
4c68830fb7 Merge branch 'main' into tab20260208 2026-02-10 09:10:52 +08:00
mll
56be3971e4 大屏预览底部留白和下钻框大小问题 2026-02-09 18:35:08 +08:00
mll
45f85ee6e8 大屏预览宽度100% 2026-02-08 22:45:11 +08:00
mll
13e436bbb5 BI大屏下钻和日期拆分 2026-02-08 20:35:07 +08:00
26 changed files with 2617 additions and 314 deletions

View File

@@ -5,5 +5,7 @@ const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"',
BASE_API: '"http://127.0.0.1:48090"'
// BASE_API: '"http://192.168.1.241:8080/prod-api"'
})

View File

@@ -8,6 +8,14 @@ export function login (data) {
})
}
export function logincas (data) {
return request({
url: 'accessUser/logincas',
method: 'post',
data
})
}
export function logout () {
return request({
url: 'accessUser/logout',
@@ -15,6 +23,18 @@ export function logout () {
})
}
export function outlogcas (data) {
console.log(data)
return request({
url: 'accessUser/outlogcas',
method: 'post',
data
})
}
// 登录之后 根据旧修改密码
export function reqUpdatePassword (data) {
return request({

View File

@@ -271,7 +271,6 @@ export default {
return { top: top, left: left }
},
objToOne(obj) {
console.log(obj)
let tmpData = {}
for (let index in obj) {
if (typeof obj[index] == 'object' && !this.isArrayFn(obj[index])) {

View File

@@ -6,7 +6,6 @@ export default router
const whiteList = ['/login', '/aj/**', '/el/**', '/bigscreen/viewer', '/excelreport/viewer']
// 判断是否需要登录权限 以及是否登录
router.beforeEach((to, from, next) => {
NProgress.start()
let token = getToken();
let lideeUser = getAccessUser();
@@ -18,6 +17,7 @@ router.beforeEach((to, from, next) => {
next()
}
}else {
if (whiteList.includes(to.path)) {
next()
}else {

View File

@@ -250,7 +250,8 @@ export const constantRouterMap = [
component: () => import('@/views/bigscreenDesigner/viewer'),
hidden: true,
meta: {
requireAuth: true
requireAuth: true,
keepAlive: true,
}
},
{

View File

@@ -32,6 +32,7 @@ const user = {
const repCode = response.repCode
const repData = response.repData
if (repCode === '0000') {
commit('SET_TOKEN', repData.token)
commit('SET_ACCESSUSER', repData.accessUser)
resolve(response)

View File

@@ -1,5 +1,6 @@
import axios from 'axios'
import { Message, MessageBox } from 'element-ui'
import router from '../router'
import store from '../store'
import { getToken } from '@/utils/auth'
// 创建axios实例
@@ -41,7 +42,13 @@ service.interceptors.response.use(
// sessionStorage.clear()
// localStorage.clear()
localStorage.removeItem('AJReportToken')
window.location.href = "/";
router.push({
path: '/login',
query: {
redirect: location.hash.substring(1)
}
})
// window.location.href = "/index?redirect=" + location.hash.substring(1);
})
}
else if (res.code !== '200') {

File diff suppressed because it is too large Load Diff

View File

@@ -13,8 +13,10 @@
<el-form-item
v-if="
inputShow[item.name] &&
item.type != 'el-collapse'&&
item.type != 'dycustComponents' &&
item.type != 'dynamic-add-table'
item.type != 'dynamic-add-table'&&
(item.name != 'is_drill_drown' || hideDrill === false)
"
:label="item.label"
:prop="item.name"
@@ -155,7 +157,7 @@
<el-button type="primary" @click="saveData">确 定</el-button>
</span>
</el-dialog>
<el-dialog
title="代码编辑"
:visible.sync="methodsVisible"
@@ -173,6 +175,15 @@
</span>
</el-dialog>
</el-form-item>
<el-collapse v-if="inputShow[item.name] && item.type === 'el-collapse'">
<el-collapse-item
class="collapseItem1"
:title="item.label"
@click.prevent.native="handleCollapse"
:name="item.label"
>
</el-collapse-item>
</el-collapse>
<dynamicComponents
v-if="item.type == 'dycustComponents' && inputShow[item.name]"
v-model="formData[item.name]"
@@ -336,6 +347,7 @@
<script>
import ColorPicker from "./colorPicker.vue";
import vueJsonEditor from "vue-json-editor";
import "codemirror/lib/codemirror.css"; // 核心样式
import "codemirror/theme/cobalt.css"; // 引入主题后还需要在 options 中指定主题才会生效
// language
@@ -354,7 +366,7 @@ import componentLinkage from './componentLinkage';
import imageSelect from './imageSelect';
import multiIframeManager from './multiIframeManager.vue';
export default {
name: "DynamicForm",
name: "dynamic-form",
components: {
imageSelect,
ColorPicker,
@@ -390,6 +402,10 @@ export default {
widgetIndex: {
type: Number,
default: -1
},
hideDrill:{
type: Boolean,
default:false,
}
},
data() {
@@ -399,6 +415,8 @@ export default {
dialogVisibleStaticData: false,
methodsVisible: false,
validationRules: "",
optionsJavascript: {
mode: "text/javascript",
tabSize: 2, // 缩进格式
@@ -416,6 +434,7 @@ export default {
value(newValue, oldValue) {
this.formData = newValue || {};
},
options(val) {
this.setDefaultValue();
this.isShowData();
@@ -427,6 +446,12 @@ export default {
},
mounted() {},
methods: {
handleCollapse(){
this.$emit('handleCollapse',this.formData)
},
changeDrillData(val){
this.changed(val, 'drill_drown_setting')
},
onJsonChange(val) {
console.log(val);
},
@@ -442,7 +467,6 @@ export default {
} else {
this.$set(this.formData, key, val);
}
this.$emit("onChanged", this.formData);
// key为当前用户操作的表单组件
for (let i = 0; i < this.options.length; i++) {
@@ -465,6 +489,7 @@ export default {
handleClose() {
this.dialogVisibleStaticData = false;
this.methodsVisible = false;
},
// 组件属性 数据是否展示动态还是静态数据
isShowData() {
@@ -480,8 +505,10 @@ export default {
}
}
data.forEach((el) => {
if (el.relactiveDomValue != currentData.value) {
if (el.relactiveDomValue != currentData.value&&el.name!=='drill_drown_setting') {
this.inputShow[el.name] = false;
}else if(el.name==='drill_drown_setting'){
this.inputShow[el.name] = el.relactiveDomValue == this.formData[el.relactiveDom];
}
});
},
@@ -494,6 +521,7 @@ export default {
this.formData[this.options[i].name] = this.deepClone(
this.options[i].value
);
} else if (Object.prototype.toString.call(obj) == "[object Array]") {
for (let j = 0; j < obj.length; j++) {
const list = obj[j].list;
@@ -515,6 +543,12 @@ export default {
</script>
<style lang="scss">
.dialogDrillDrown.el-dialog__wrapper .el-dialog{
.el-dialog__body{
max-height:calc(100vh - 60px) !important;
overflow-y:hidden !important;
}
}
.el-form-item {
margin-bottom: 5px;
}
@@ -547,4 +581,9 @@ export default {
background: transparent;
padding-bottom: 0;
}
.collapseItem1 .el-collapse-item__arrow.is-active{
cursor:pointer;
color: #bcc9d4;
transform:rotate(0)
}
</style>

View File

@@ -259,7 +259,7 @@ export default {
},
// 列表查询
async handleQueryPageList() {
debugger
// 将特殊参数值urlcode处理
// 默认的排序
if (

View File

@@ -312,13 +312,34 @@
label="配置"
>
<dynamicForm
ref="formData"
ref="formData1"
:options="widgetOptions.setup"
:layer-widget="layerWidget"
:widget-index="widgetIndex"
:widget-params-config="widgetParamsConfig"
@handleCollapse="handleCollapse"
@onChanged="(val) => widgetValueChanged('setup', val)"
/>
<!-- 如果当前选中的是 Tabs 内部子组件,在配置表单底部显示删除按钮 -->
<div
v-if="innerWidgetSelected"
style="text-align: right; margin-top: 12px;"
>
<el-popconfirm
title="确定删除该子组件吗?"
@onConfirm="deleteInnerWidget"
@confirm="deleteInnerWidget"
>
<el-button
slot="reference"
type="danger"
size="mini"
plain
>
删除该子组件
</el-button>
</el-popconfirm>
</div>
</el-tab-pane>
<el-tab-pane
v-if="isNotNull(widgetOptions.data)"
@@ -344,7 +365,15 @@
</el-tab-pane>
</el-tabs>
</div>
<el-dialog
title="下钻配置"
:visible.sync="dialogVisibleDrillDrown"
width="100%"
class="dialogDrillDrown"
>
<drill-drown-setting @submitDrillDrownData="submitDrillDrownData" :screenData="screenData"></drill-drown-setting>
</el-dialog>
<content-menu
:visible.sync="visibleContentMenu"
:style-obj="styleObj"
@@ -369,7 +398,7 @@ import dynamicForm from "./components/dynamicForm.vue";
import draggable from "vuedraggable";
import VueRulerTool from "vue-ruler-tool"; // 大屏设计页面的标尺插件
import contentMenu from "./components/contentMenu";
import DrillDrownSetting from "./components/drillDrownSetting";
export default {
name: "Login",
components: {
@@ -378,11 +407,13 @@ export default {
widget,
dynamicForm,
contentMenu,
DrillDrownSetting
},
mixins: [mixin],
data() {
return {
grade: false,
dialogVisibleDrillDrown:false,
layerWidget: [],
widgetTools: widgetTools, // 左侧工具栏的组件图标将js变量加入到当前作用域
widthLeftForTools: 200, // 左侧工具栏宽度
@@ -390,7 +421,10 @@ export default {
widthLeftForOptions: 300, // 右侧属性配置区
widthPaddingTools: 18,
toolIsShow: true, // 左侧工具栏是否显示
screenData:{
dashboard: { },
widgets: []
},
bigscreenWidth: 1920, // 大屏设计的大小
bigscreenHeight: 1080,
@@ -498,6 +532,17 @@ export default {
});
},
methods: {
submitDrillDrownData(data){
this.dialogVisibleDrillDrown=false
this.$refs.formData1.changeDrillData(data)
},
handleCollapse(val){
this.dialogVisibleDrillDrown=true
this.screenData=val.drill_drown_setting?val.drill_drown_setting:{
dashboard: { },
widgets: []
}
},
// 获取图层数据
getLayerData(val) {
const layerWidgetArr = [];
@@ -510,6 +555,7 @@ export default {
if (val[i].value.paramsKeys) {
obj.paramsKeys = val[i].value.paramsKeys;
}
const options = val[i].options["setup"];
options.forEach((el) => {
if (el.name == "layerName") {
@@ -641,6 +687,7 @@ export default {
options: tool.options,
};
// 处理默认值
console.log(widgetType)
const widgetJsonValue = this.getWidgetConfigValue(widgetJson);
widgetJsonValue.value.position.left =
@@ -663,7 +710,6 @@ export default {
widgetJson.value.position
);
this.setWidgetConfigValue(widgetJson.options.data, widgetJson.value.data);
return widgetJson;
},
setWidgetConfigValue(config, configValue) {
@@ -695,9 +741,9 @@ export default {
const rootIndex = payload.rootWidgetIndex;
const widget = this.widgets[rootIndex];
if (!widget || !widget.value || !widget.value.position) return;
// 选中 Tabs 组件本身,触发蓝色点框和右侧配置
this.innerWidgetSelected = null;
this.widgetIndex = rootIndex;
this.setOptionsOnClickWidget(rootIndex);
this.widgetsClickFocus(rootIndex);
const evt = payload.event;
const workbenchRect = document.getElementById('workbench') && document.getElementById('workbench').getBoundingClientRect();
if (!workbenchRect) return;
@@ -774,12 +820,58 @@ export default {
*/
setOptionsOnClickInnerWidget(payload) {
if (!payload) return;
const { rootWidgetIndex, tabIndex, childIndex, widget } = payload;
console.log('setOptionsOnClickInnerWidget payload:', payload);
const { rootWidgetIndex, tabIndex, childIndex } = payload;
const rootIndex = typeof rootWidgetIndex === "number" ? rootWidgetIndex : 0;
const rootWidget = this.widgets[rootIndex];
if (!rootWidget || !rootWidget.value || !rootWidget.value.setup) return;
if (!rootWidget || !rootWidget.value) return;
// 记录当前选中的内部子组件路径
// 1. 兼容两种存储位置value.setup.tabsList / options.setup 里的 tabsList
const setupObj = rootWidget.value.setup || {};
let tabsList = setupObj.tabsList;
if (!tabsList || !tabsList.length) {
const setupArr = rootWidget.options && rootWidget.options.setup ? rootWidget.options.setup : [];
const tabsItem = setupArr.find(it => it.name === "tabsList");
if (tabsItem && Array.isArray(tabsItem.value)) {
tabsList = tabsItem.value;
}
}
tabsList = tabsList || [];
const targetTab = tabsList[tabIndex];
if (!targetTab || !targetTab.children || !targetTab.children[childIndex]) {
console.warn("Tabs 子组件未找到:", { rootIndex, tabIndex, childIndex, tabsList });
return;
}
let childWidget = targetTab.children[childIndex];
// 2. 确保子组件有 options老数据里可能只有 type + value
if (!childWidget.options) {
try {
const tool = getToolByCode(childWidget.type);
if (tool && tool.options) {
childWidget.options = this.deepClone(tool.options);
} else {
console.warn("未找到子组件 options 配置:", childWidget.type);
}
} catch (e) {
console.error("为子组件补全 options 失败:", e);
}
}
// 3. 用实际 position 回填到 options.position 中,保证坐标表单显示正确
if (childWidget.options && Array.isArray(childWidget.options.position)) {
const pos = (childWidget.value && childWidget.value.position) ? childWidget.value.position : {};
childWidget.options.position.forEach((el) => {
if (pos.hasOwnProperty(el.name)) {
el.value = pos[el.name];
}
});
}
// 4. 记录当前选中的内部子组件路径 & 切换右侧到子组件配置
this.innerWidgetSelected = {
rootWidgetIndex: rootIndex,
tabIndex,
@@ -789,51 +881,53 @@ export default {
this.widgetIndex = rootIndex;
this.activeName = "first";
// 找到真正的子组件对象
const tabsList = (rootWidget.value.setup && rootWidget.value.setup.tabsList) || [];
this.widgetOptions = this.deepClone(childWidget.options || {});
},
/**
* 删除当前选中的 Tabs 内部子组件
*/
deleteInnerWidget() {
if (!this.innerWidgetSelected) return;
const { rootWidgetIndex, tabIndex, childIndex } = this.innerWidgetSelected;
const rootWidget = this.widgets[rootWidgetIndex];
if (!rootWidget || !rootWidget.value) return;
// 永远只操作“真实绑定”的数据rootWidget.value.setup.tabsList
if (!rootWidget.value.setup) {
this.$set(rootWidget.value, "setup", {});
}
if (!Array.isArray(rootWidget.value.setup.tabsList)) {
this.$set(rootWidget.value.setup, "tabsList", []);
}
const tabsList = rootWidget.value.setup.tabsList;
const targetTab = tabsList[tabIndex];
if (!targetTab || !targetTab.children || !targetTab.children[childIndex]) {
return;
}
const childWidget = widget || targetTab.children[childIndex];
if (!targetTab || !targetTab.children || !targetTab.children.length) return;
// 用实际 position 值回填到 options.position 中,保证坐标表单显示正确
if (childWidget.options && Array.isArray(childWidget.options.position)) {
const pos = childWidget.value && childWidget.value.position ? childWidget.value.position : {};
childWidget.options.position.forEach((el) => {
if (pos.hasOwnProperty(el.name)) {
el.value = pos[el.name];
}
});
}
targetTab.children.splice(childIndex, 1);
this.widgetOptions = this.deepClone(childWidget.options);
// 同步回 rootWidget 的 setup / options
rootWidget.value.setup.tabsList = tabsList;
const setupArr = rootWidget.options && rootWidget.options.setup ? rootWidget.options.setup : [];
setupArr.forEach((item) => {
if (item.name === "tabsList") {
item.value = tabsList;
}
});
// 清空内部选中状态,并回到 Tabs 本身配置
this.innerWidgetSelected = null;
this.widgetIndex = rootWidgetIndex;
this.widgetOptions = this.deepClone(rootWidget.options || {});
this.activeName = "first";
},
widgetsClick(event,index) {
console.log("widgetsClick");
// 如果点击发生在 Tabs 组件内部的子组件上,优先选中内部组件
if (event && event.target && event.target.closest) {
const tabChildWrapper = event.target.closest(".tab-child-wrapper");
const tabsRootEl = event.target.closest(".widget-tabs");
if (tabChildWrapper && tabsRootEl) {
const tabIndexAttr = tabChildWrapper.getAttribute("data-tab-index");
const childIndexAttr = tabChildWrapper.getAttribute("data-child-index");
const tabIndex = Number(tabIndexAttr);
const childIndex = Number(childIndexAttr);
if (!isNaN(tabIndex) && !isNaN(childIndex)) {
this.setOptionsOnClickInnerWidget({
rootWidgetIndex: index,
tabIndex,
childIndex,
});
return;
}
}
// 如果只是点击在 Tabs 内容区的空白处,则交给 Tabs 自己内部处理,避免整个 Tabs 组件被选中
const tabContentEl = event.target.closest(".tab-content");
if (tabContentEl && tabsRootEl) {
return;
}
// 如果 Tabs 组件内部点击由 widgetTabs 自己处理,这里只负责选中 Tabs 整体
const rootWidget = this.widgets[index];
if (rootWidget && rootWidget.type === "widget-tabs") {
this.widgetsClickFocus(index);
return;
}
//判断是否按住了Ctrl按钮表示Ctrl多选
let _this = this;

View File

@@ -70,6 +70,24 @@ export const widgetImage = {
required: false,
placeholder: '',
},
{
type: 'el-switch',
label: '开启下钻',
name: 'is_drill_drown',
required: false,
placeholder: '',
value: false,
},
{
type: 'el-collapse',
label: '下钻配置',
name: 'drill_drown_setting',
relactiveDom:'is_drill_drown',
relactiveDomValue:true,
required: false,
placeholder: '',
value:''
},
],
// 数据
data: [],

View File

@@ -66,6 +66,14 @@ export const widgetTable = {
placeholder: '',
value: false
},
{
type: 'el-switch',
label: '序号',
name: 'isIndex',
required: false,
placeholder: '',
value: false
},
{
type: 'el-input-number',
label: '边框宽度',
@@ -314,8 +322,44 @@ export const widgetTable = {
value: 1
},
]
}
},
],
{
type: 'el-switch',
label: '开启下钻',
name: 'is_drill_drown',
required: false,
placeholder: '',
value: false,
},
{
type: 'el-collapse',
label: '下钻配置',
name: 'drill_drown_setting',
relactiveDom:'is_drill_drown',
relactiveDomValue:true,
required: false,
placeholder: '',
value:''
},
{
type: 'el-switch',
label: '阅读模式',
name: 'is_read_mode',
required: false,
placeholder: '',
value: false,
},
{
type: 'vue-color',
label: '阅读模式颜色',
name: 'read_color',
relactiveDom:'is_read_mode',
relactiveDomValue:true,
required: false,
placeholder: '',
value:''
},
{
type: 'dynamic-add-table',
label: '',

View File

@@ -150,6 +150,24 @@ export const widgetText = {
placeholder: '',
value: false,
},
{
type: 'el-switch',
label: '开启下钻',
name: 'is_drill_drown',
required: false,
placeholder: '',
value: false,
},
{
type: 'el-collapse',
label: '下钻配置',
name: 'drill_drown_setting',
relactiveDom:'is_drill_drown',
relactiveDomValue:true,
required: false,
placeholder: '',
value:''
},
[
{
name: '自定义样式设置',

View File

@@ -0,0 +1,179 @@
<!--
* @Author: qianlishi qianlishi@anji-plus.com
* @Date: 2023-03-06 15:38:10
* @LastEditors: qianlishi qianlishi@anji-plus.com
* @LastEditTime: 2023-04-20 13:54:57
-->
<template>
<el-date-picker
:style="styleObj"
v-model="timeValue"
:value-format="valueFormat"
:picker-options="datetimeRangePickerOptions"
:type="dateType"
@[eventChange]="change"
/>
</template>
<script>
import {
originWidgetLinkageLogic,
targetWidgetLinkageLogic,
} from "@/views/bigscreenDesigner/designer/linkageLogic";
import miment from 'miment'
export default {
name: "WidgetFormTime",
props: {
value: Object,
ispreview: Boolean,
widgetIndex: {
type: Number,
default: 0,
},
},
data() {
return {
timeValue: "",
optionsStyle: {},
optionsData: {},
optionsSetup: {},
//日期时间快捷选项
datetimeRangePickerOptions: {
shortcuts: [{
text: '今天',
onClick(picker) {
const start = new Date(new Date(new Date().getTime()).setHours(0, 0, 0, 0));
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
picker.$emit('pick', [start, end]);
}
}, {
text: '昨天',
onClick(picker) {
const start = new Date(new Date(new Date().getTime() - 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0));
const end = new Date(new Date(new Date().getTime() - 24 * 60 * 60 * 1000).setHours(23, 59, 59, 999));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一周',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date(new Date(new Date().getTime() + 24 * 60 * 60 * 1000));
start.setTime(miment().add(-6, 'DD').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date();
start.setTime(miment().add(-1, 'MM').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date();
start.setTime(miment().add(-3, 'MM').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近半年',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date();
start.setTime(miment().add(-6, 'MM').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一年',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date();
start.setTime(miment().add(-1, 'YY').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}],
// disabledDate(time){
// return time.getTime() > Date.now()
// }
}
};
},
computed: {
styleObj() {
return {
position: this.ispreview ? "absolute" : "static",
width: this.optionsStyle.width + "px",
height: this.optionsStyle.height + "px",
left: this.optionsStyle.left + "px",
top: this.optionsStyle.top + "px",
background: this.optionsSetup.select_background,
};
},
eventChange() {
return "change";
},
dateType() {
return this.optionsSetup.dateType || 'datetimerange';
},
valueFormat() {
return this.dateType === 'daterange' ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss';
},
allComponentLinkage() {
return this.$store.state.designer.allComponentLinkage;
},
},
watch: {
value: {
handler(val) {
this.optionsSetup = val.setup;
this.optionsData = val.data;
this.optionsStyle = val.position;
},
deep: true,
},
},
mounted() {
this.optionsSetup = this.value.setup;
this.optionsData = this.value.data;
this.optionsStyle = this.value.position;
targetWidgetLinkageLogic(this); // 联动-目标组件逻辑
},
methods: {
change(event) {
const formTimeData = {}
formTimeData['startTime'] = event[0] //startTime
formTimeData['endTime'] = event[1] //endTime
originWidgetLinkageLogic(this, true, {
currentData: formTimeData,
}); // 联动-源组件逻辑
},
},
};
</script>
<style scoped lang="scss">
.el-select {
height: 100%;
.el-input {
height: 100%;
.el-input__inner {
height: 100%;
background: inherit;
color: inherit;
&::placeholder {
color: inherit;
}
}
}
}
</style>

View File

@@ -5,14 +5,26 @@
* @LastEditTime: 2023-04-20 13:54:57
-->
<template>
<div class="pickWrap">
<el-date-picker
:style="styleObj"
v-model="timeValue"
:value-format="valueFormat"
:picker-options="datetimeRangePickerOptions"
:type="dateType"
@[eventChange]="change"
class="start-picker date-picker"
:style="styleObj"
v-model="startTime"
:value-format="valueFormat"
:type="dateType=='datetimerange'?'datetime':'date'"
@[eventChange]="change"
/>
<el-date-picker
class="date-picker"
:style="{...styleObj,left:styleObj.leftEnd,width:styleObj.widthEnd}"
v-model="endTime"
:value-format="valueFormat"
:type="dateType=='datetimerange'?'datetime':'date'"
@[eventChange]="change"
/>
</div>
</template>
<script>
import {
@@ -33,85 +45,23 @@ export default {
},
data() {
return {
timeValue: "",
optionsStyle: {},
optionsData: {},
optionsSetup: {},
//日期时间快捷选项
datetimeRangePickerOptions: {
shortcuts: [{
text: '今天',
onClick(picker) {
const start = new Date(new Date(new Date().getTime()).setHours(0, 0, 0, 0));
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
picker.$emit('pick', [start, end]);
}
}, {
text: '昨天',
onClick(picker) {
const start = new Date(new Date(new Date().getTime() - 24 * 60 * 60 * 1000).setHours(0, 0, 0, 0));
const end = new Date(new Date(new Date().getTime() - 24 * 60 * 60 * 1000).setHours(23, 59, 59, 999));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一周',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date(new Date(new Date().getTime() + 24 * 60 * 60 * 1000));
start.setTime(miment().add(-6, 'DD').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date();
start.setTime(miment().add(-1, 'MM').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date();
start.setTime(miment().add(-3, 'MM').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近半年',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date();
start.setTime(miment().add(-6, 'MM').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}, {
text: '最近一年',
onClick(picker) {
const end = new Date(new Date(new Date().getTime()).setHours(23, 59, 59, 999));
const start = new Date();
start.setTime(miment().add(-1, 'YY').stamp());
new Date(start.setHours(0, 0, 0, 0));
picker.$emit('pick', [start, end]);
}
}],
// disabledDate(time){
// return time.getTime() > Date.now()
// }
}
endTime:null,
startTime:null,
};
},
computed: {
styleObj() {
return {
position: this.ispreview ? "absolute" : "static",
width: this.optionsStyle.width + "px",
width: this.optionsStyle.width/2+12 + "px",
widthEnd: this.optionsStyle.width/2-12 + "px",
height: this.optionsStyle.height + "px",
left: this.optionsStyle.left + "px",
leftEnd: this.optionsStyle.left+this.optionsStyle.width/2+12 + "px",
top: this.optionsStyle.top + "px",
background: this.optionsSetup.select_background,
};
@@ -149,8 +99,8 @@ export default {
methods: {
change(event) {
const formTimeData = {}
formTimeData['startTime'] = event[0] //startTime
formTimeData['endTime'] = event[1] //endTime
formTimeData['startTime'] = this.startTime //event[0] //startTime
formTimeData['endTime'] = this.endTime //event[1] //endTime
originWidgetLinkageLogic(this, true, {
currentData: formTimeData,
}); // 联动-源组件逻辑
@@ -159,21 +109,20 @@ export default {
};
</script>
<style scoped lang="scss">
.el-select {
height: 100%;
.el-input {
height: 100%;
.el-input__inner {
height: 100%;
background: inherit;
color: inherit;
&::placeholder {
color: inherit;
}
.start-picker{
display:flex;
&:after{
content:'-';
width:24px;
transform:translate(7px,5px)
}
}
}
.pickWrap{
display:flex;
&::v-deep() {
.el-input__prefix{
height:28px;
}
}
}
</style>

View File

@@ -29,6 +29,7 @@
class="tab-content"
data-tab-content
:style="contentStyle"
@click.capture="onTabContentClick($event, index)"
@drop="handleTabDrop($event, tab, index)"
@dragover.capture.prevent="handleTabDragOver($event)"
@dragover.prevent="handleTabDragOver($event)"
@@ -42,8 +43,8 @@
:key="childWidget.value.widgetId || `child-${index}-${childIndex}`"
class="tab-child-draggable"
:style="{
left: (childWidget.value.position && typeof childWidget.value.position.left === 'number') ? childWidget.value.position.left + 'px' : '0px',
top: (childWidget.value.position && typeof childWidget.value.position.top === 'number') ? childWidget.value.position.top + 'px' : '0px',
marginLeft: (childWidget.value.position && typeof childWidget.value.position.left === 'number') ? childWidget.value.position.left + 'px' : '0px',
marginTop: (childWidget.value.position && typeof childWidget.value.position.top === 'number') ? childWidget.value.position.top + 'px' : '0px',
width: (childWidget.value.position && childWidget.value.position.width) ? childWidget.value.position.width + 'px' : '100px',
height: (childWidget.value.position && childWidget.value.position.height) ? childWidget.value.position.height + 'px' : '50px',
}"
@@ -52,9 +53,15 @@
class="tab-child-wrapper"
:data-tab-index="index"
:data-child-index="childIndex"
@mousedown.capture="onTabChildMouseDown($event, index, childIndex)"
@contextmenu.prevent.stop="handleChildWidgetRightClick($event, childIndex, index)"
>
<!-- 选中子组件的小手柄点击后触发右侧显示该子组件配置 -->
<span
class="tab-child-select-handle"
title="选中并配置该组件"
@mousedown.stop.prevent="onTabChildHandleDown(index, childIndex)"
>
<i class="el-icon-setting tab-child-select-icon"></i>
</span>
<component
:is="getComponentName(childWidget.type)"
:value="childWidget.value"
@@ -62,14 +69,6 @@
:ispreview="ispreview"
class="tab-child-component"
/>
<span
class="tab-child-delete"
title="删除组件"
@mousedown.stop
@click.stop="removeChildWidget(index, childIndex)"
>
×
</span>
</div>
</div>
</template>
@@ -310,7 +309,6 @@ export default {
* 触发 Tabs 内部子组件选中事件,让设计器右侧展示该子组件的配置
*/
emitChildActivated(tabIndex, childIndex) {
// 预览模式下不需要触发设计事件
if (this.ispreview) return;
const currentTabsList = this.tabsList;
const targetTab = currentTabsList[tabIndex];
@@ -323,11 +321,50 @@ export default {
widget: childWidget,
});
},
/**
* tab 内容区域点击:单击子组件 => 选中子组件;双击空白 => 选中整个 Tabs
*/
onTabContentClick(evt, tabIndex) {
if (this.ispreview || !evt.target || !evt.target.closest) return;
// 1如果点在某个子组件包装层上优先选中该子组件
const wrapper = evt.target.closest(".tab-child-wrapper");
if (wrapper) {
const childIndexAttr = wrapper.getAttribute("data-child-index");
const childIndex = Number(childIndexAttr);
if (!isNaN(childIndex)) {
this.emitChildActivated(tabIndex, childIndex);
}
evt.stopPropagation();
return;
}
// 2双击内容区空白通知外层选中整个 Tabs
const contentEl = evt.currentTarget;
const hasChildWrapper = contentEl.querySelector(".tab-child-wrapper");
if (!hasChildWrapper && evt.detail === 2) {
this.$emit("tabsContentDblClick", { tabIndex, event: evt });
evt.stopPropagation();
return;
}
},
/**
* 点击左上角选择手柄:只做“选中内部子组件”的动作
*/
onTabChildHandleDown(tabIndex, childIndex) {
if (this.ispreview) return;
this.emitChildActivated(tabIndex, childIndex);
},
setActiveTab() {
const tabs = this.tabsList;
if (tabs && tabs.length > 0) {
this.activeTab = tabs[0].name || 'tab0';
if (!tabs || tabs.length === 0) return;
// 如果当前 activeTab 还在 tabs 中,则保持不变,避免每次数据变化都跳回第一个标签
if (this.activeTab) {
const exist = tabs.some((t, i) => (t.name || `tab${i}`) === this.activeTab);
if (exist) return;
}
// 当前没有选中标签,或原选中标签已被删除时,才默认选中第一个
this.activeTab = tabs[0].name || 'tab0';
},
handleTabClick(tab) {
const currentTab = this.tabsList.find(t => (t.name || `tab${this.tabsList.indexOf(t)}`) === tab.name);
@@ -445,24 +482,9 @@ export default {
// 处理默认值
const widgetJsonValue = this.getWidgetConfigValue(widgetJson);
// 设置位置相对于tab内容区域
widgetJsonValue.value.position.left = x - widgetJsonValue.value.position.width / 2;
widgetJsonValue.value.position.top = y - widgetJsonValue.value.position.height / 2;
// 确保位置在容器内
if (widgetJsonValue.value.position.left < 0) {
widgetJsonValue.value.position.left = 0;
}
if (widgetJsonValue.value.position.top < 0) {
widgetJsonValue.value.position.top = 0;
}
if (widgetJsonValue.value.position.left + widgetJsonValue.value.position.width > this.optionsStyle.width) {
widgetJsonValue.value.position.left = this.optionsStyle.width - widgetJsonValue.value.position.width;
}
if (widgetJsonValue.value.position.top + widgetJsonValue.value.position.height > this.optionsStyle.height) {
widgetJsonValue.value.position.top = this.optionsStyle.height - widgetJsonValue.value.position.height;
}
// Tabs 内部子组件:默认边距统一从 0 开始,由右侧“坐标”面板控制间距
widgetJsonValue.value.position.left = 0;
widgetJsonValue.value.position.top = 0;
// 生成唯一ID
const uuid = Number(Math.random().toString().substr(2)).toString(36);
@@ -537,14 +559,9 @@ export default {
options: this.deepClone(tool.options),
};
const widgetJsonValue = this.getWidgetConfigValue(widgetJson);
widgetJsonValue.value.position.left = Math.max(0, x - widgetJsonValue.value.position.width / 2);
widgetJsonValue.value.position.top = Math.max(0, y - widgetJsonValue.value.position.height / 2);
if (widgetJsonValue.value.position.left + widgetJsonValue.value.position.width > this.optionsStyle.width) {
widgetJsonValue.value.position.left = this.optionsStyle.width - widgetJsonValue.value.position.width;
}
if (widgetJsonValue.value.position.top + widgetJsonValue.value.position.height > this.optionsStyle.height) {
widgetJsonValue.value.position.top = this.optionsStyle.height - widgetJsonValue.value.position.height;
}
// 同样,外层 drop 到 Tabs 上时,内部子组件也从 0 边距起步
widgetJsonValue.value.position.left = 0;
widgetJsonValue.value.position.top = 0;
const uuid = Number(Math.random().toString().substr(2)).toString(36);
widgetJsonValue.value.widgetId = uuid;
widgetJsonValue.value.widgetCode = dragWidgetCode;
@@ -611,16 +628,19 @@ export default {
* 根节点捕获:仅当点击在标题栏时发出事件,让设计器拖动整块 Tabs外层 avue-draggable 已禁用)
*/
onTabsRootCapture(evt) {
if (this.ispreview) return;
if (evt.target && evt.target.closest && evt.target.closest('.el-tabs__header')) {
if (this.ispreview || !evt.target || !evt.target.closest) return;
const headerEl = evt.target.closest(".el-tabs__header");
if (headerEl) {
// 点击在 Tabs 头部:阻止事件冒泡到外层 draggable并通知外层开始拖动整块 Tabs
evt.stopPropagation();
this.$emit('tabsHeaderMouseDown', evt);
this.$emit("tabsHeaderMouseDown", evt);
}
},
onTabChildMouseDown(event, tabIndex, childIndex) {
if (event.target && event.target.closest && event.target.closest('.tab-child-delete')) {
return; // 点在删除按钮上,不拦截,删除按钮的 @mousedown.stop 会阻止冒泡到外层
}
console.log('onTabChildMouseDown:', tabIndex, childIndex);
event.preventDefault();
event.stopPropagation();
// 选中内部组件并通知外层暂时禁用拖拽
@@ -726,35 +746,6 @@ export default {
// 通知外层 Widget内部拖拽结束恢复外层 avue-draggable
this.$emit("innerDragEnd");
},
handleChildWidgetRightClick(event, childIndex, tabIndex) {
// 处理子组件右键 - 支持直接删除
event.stopPropagation();
this.$confirm("确定删除该组件吗?", "提示", {
type: "warning",
confirmButtonText: "确定",
cancelButtonText: "取消",
})
.then(() => {
this.removeChildWidget(tabIndex, childIndex);
})
.catch(() => {});
},
/**
* 从指定 tab 中移除一个子组件
*/
removeChildWidget(tabIndex, childIndex) {
const currentTabsList = this.tabsList;
const targetTab = currentTabsList[tabIndex];
if (!targetTab || !targetTab.children) return;
targetTab.children.splice(childIndex, 1);
this.optionsSetup.tabsList = currentTabsList;
if (!this.value.setup) {
this.$set(this.value, "setup", {});
}
this.value.setup.tabsList = currentTabsList;
this.$emit("input", this.value);
},
// 将组件类型字符串转换为组件名称
getComponentName(type) {
// 将 widget-text 转换为 widgetText
@@ -808,34 +799,39 @@ export default {
position: relative;
}
.tab-child-select-handle {
position: absolute;
top: -6px;
right: -2px;
width: 18px;
height: 18px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.45);
z-index: 30;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.tab-child-select-handle:hover {
background: rgba(64, 158, 255, 0.9);
}
.tab-child-select-icon {
font-size: 14px;
color: #fff;
}
.tab-child-component {
width: 100%;
height: 100%;
}
.tab-child-delete {
position: absolute;
right: 2px;
top: 2px;
width: 14px;
height: 14px;
line-height: 14px;
text-align: center;
font-size: 12px;
border-radius: 50%;
background: rgba(0, 0, 0, 0.4);
color: #fff;
cursor: pointer;
z-index: 20;
}
.tab-child-delete:hover {
background: rgba(255, 0, 0, 0.8);
}
.tab-content-empty {
width: 100%;
height: 100%;
min-height: 200px;
display: flex;
align-items: center;
justify-content: center;

View File

@@ -5,6 +5,7 @@
:value="value"
:ispreview="true"
:widget-index="index"
@oepnTheDrillView="$emit('oepnTheDrillView',$event)"
/>
</div>
</template>
@@ -58,7 +59,6 @@ import widgetChinaMap from "./map/widgetChinaMap.vue";
import widgetGlobalMap from "./map/widgetGlobalMap.vue";
import widgetBarStackMoreShowChart from "./bar/widgetBarStackMoreShowChart.vue";
import widgetBarLineSingleChart from "./barline/widgetBarLineSingleChart.vue";
export default {
name: "WidgetTemp",
components: {

View File

@@ -1,5 +1,5 @@
<template>
<div class="imagebox" :style="styleColor">
<div class="imagebox" :style="styleColor" @click="handleClick" >
<img
:class="transStyle.startRotate ? 'startImg' : ''"
:style="imgStyle"
@@ -18,7 +18,8 @@ export default {
},
data() {
return {
options: {}
options: {},
optionsSetup:{}
};
},
computed: {
@@ -50,15 +51,26 @@ export default {
value: {
handler(val) {
this.options = val;
this.optionsSetup = val.setup;
},
deep: true
}
},
created() {
this.options = this.value;
this.optionsSetup = this.value.setup;
},
mounted() {},
methods: {}
mounted() {
},
methods: {
handleClick(){
console.log(this.optionsSetup)
if(!!this.optionsSetup.is_drill_drown){
this.$emit('oepnTheDrillView',this.optionsSetup.drill_drown_setting)
}
},
}
};
</script>

View File

@@ -3,26 +3,45 @@
<superslide v-if="hackReset" :options="options" class="txtScroll-top" ref="superslide">
<!--表头-->
<div class="title">
<div
v-if="isIndex"
:style="[headerTableStyle,{width:'50px',flexShrink: 0}, tableRowHeight()]">
序号
</div>
<div v-for="(item, index) in header" :key="index"
:style="[headerTableStyle, tableFiledWidth(index), tableRowHeight()]">
{{ item.name }}
</div>
</div>
<!--数据-->
<div class="bd">
<div class="bd" @click="handleClick">
<ul class="infoList">
<li v-for="(item, index) in list" :key="index" :style="tableRowHeight()">
<div v-for="(itemChild, idx) in header"
<li v-for="(item, index) in list" :key="index" :style="tableRowHeight()">
<div
@mouseenter="handleMouseSeen(index, -1)" @mouseleave="handleMouseLeave"
v-if="isIndex"
:style="[
bodyTableStyle,
tableReadColor(index, -1),{width:'50px',flexShrink: 0},
bodyTable(index),
tableRowHeight()
]"
>
{{ index + 1 }}
</div>
<div @mouseenter="handleMouseSeen(index, idx)" @mouseleave="handleMouseLeave" v-for="(itemChild, idx) in header"
:key="idx"
:style="[
bodyTableStyle,
bodyTable(index),
tableFiledWidth(idx),
tableReadColor(index, idx),
tableRowHeight()
]"
>
{{ item[itemChild.key] }}
</div>
</li>
</ul>
</div>
@@ -98,10 +117,12 @@ export default {
},
header: [],
list: [],
hoverIndex: {},
optionsSetUp: {},
optionsPosition: {},
optionsData: {},
flagInter: null,
isIndex: false,
// 新增导出对话框相关数据
exportDialogVisible: false,
exporting: false,
@@ -131,6 +152,7 @@ export default {
},
headerTableStyle() {
const headStyle = this.optionsSetUp;
this.isIndex=headStyle.isIndex
return {
"text-align": headStyle.textAlignHeader,
"font-size": headStyle.fontSizeHeader + "px",
@@ -216,6 +238,7 @@ export default {
value: {
handler(val) {
this.optionsSetUp = val.setup;
this.optionsPosition = val.position;
this.optionsData = val.data;
this.initData();
@@ -238,6 +261,17 @@ export default {
window.removeEventListener('resize', this.handleResize);
},
methods: {
handleMouseSeen(index, idx){
this.hoverIndex = {index, idx}
},
handleMouseLeave(){
this.hoverIndex = {}
},
handleClick(){
if(!!this.optionsSetUp.is_drill_drown){
this.$emit('oepnTheDrillView',this.optionsSetUp.drill_drown_setting)
}
},
initData() {
this.handlerRollFn();
this.handlerHead();
@@ -341,6 +375,14 @@ export default {
}
return styleJson;
},
tableReadColor(index, idx){
let styleJson = {};
if (this.optionsSetUp.is_read_mode && (this.hoverIndex.index === index || this.hoverIndex.idx === idx)) {
styleJson["background-color"] = this.optionsSetUp.read_color;console.log(styleJson["background-color"],this.hoverIndex,1231231)
}
return styleJson;
},
// 添加的方法 - 设备类型检测
checkDeviceType() {
this.screenWidth = window.innerWidth;

View File

@@ -5,7 +5,7 @@
* @Last Modified time: 2022-3-14 14:04:24
!-->
<template>
<div class="text" :style="computedStyleColor">{{ styleColor.text }}</div>
<div class="text" @click="handleClick" :style="computedStyleColor">{{ styleColor.text }}</div>
</template>
<script>
import {targetWidgetLinkageLogic} from "@/views/bigscreenDesigner/designer/linkageLogic";
@@ -84,6 +84,11 @@ export default {
this.setOptionsData();
},
methods: {
handleClick(){
if(!!this.optionsSetup.is_drill_drown){
this.$emit('oepnTheDrillView',this.optionsSetup.drill_drown_setting)
}
},
// 根据条件应用样式
getConditionalStyle() {
const setup = this.optionsSetup || {};

View File

@@ -21,6 +21,7 @@
@innerDragStart="handleInnerDragStart"
@innerDragEnd="handleInnerDragEnd"
@tabsHeaderMouseDown="handleTabsHeaderMouseDown"
@tabsContentDblClick="handleTabsContentDblClick"
/>
</avue-draggable>
</template>
@@ -178,10 +179,9 @@ export default {
return this.value.position.zIndex || 1;
},
widgetDisabled() {
// Tabs 必须禁用外层拖拽内部子组件才能选中Tabs 整体拖动改由标题栏单独处理
if (this.type === 'widget-tabs') {
return true;
}
// 统一走一套逻辑:
// - 当内部子组件正在拖拽时禁用外层拖拽,避免误拖整块组件
// - 其余情况按组件自身的 disabled 控制
return this.value.position.disabled || this.innerDragging || false;
},
},
@@ -239,6 +239,7 @@ export default {
* 接收 Tabs 内部子组件发出的激活事件,并转发给设计器主页面
*/
handleChildActivated(payload) {
console.log('handleChildActivated in widget.vue:', payload);
const info = Object.assign({}, payload || {});
if (info.rootWidgetIndex === undefined || info.rootWidgetIndex === null) {
info.rootWidgetIndex = this.index;
@@ -255,6 +256,11 @@ export default {
handleTabsHeaderMouseDown(evt) {
this.$emit('onTabsHeaderMouseDown', { event: evt, rootWidgetIndex: this.index });
},
// Tabs 内容区双击空白时,也视为选中整个 Tabs
handleTabsContentDblClick(payload) {
if (!payload || !payload.event) return;
this.$emit('onTabsHeaderMouseDown', { event: payload.event, rootWidgetIndex: this.index });
},
},
};
</script>

View File

@@ -0,0 +1,159 @@
<!--
* @Descripttion: 大屏设计器 -- 预览
* @Author: Devli
* @Date: 2021-3-13 11:04:24
* @Last Modified by: Raod
* @Last Modified time: 2022-5-6 11:04:24
!-->
<template>
<div class="layout drillDrownViewLayout">
<div :style="bigScreenStyle">
<widget
v-for="(widget, index) in widgets"
:key="index"
v-model="widget.value"
:index="index"
:type="widget.type"
/>
</div>
</div>
</template>
<script>
import widget from "../designer/widget/temp";
import { detailDashboard } from "@/api/bigscreen";
export default {
name: "Login",
components: {
widget
},
data() {
return {
bigScreenStyle: {},
dashboard:{},
widgets: [],
viewData:{}
};
},
props:{
screenData:{
type:Object,
default:()=>{
return {
dashboard: { },
widgets: []
}
}
}
},
watch: {
screenData: {
handler(val) {
this.viewData=val
this.getData();
},
deep: true,
immediate:true
}
},
mounted() {
this.viewData=this.screenData
this.getData();
window.onresize=this.Debounce(this.setScale,500);
},
methods: {
async getData() {
const data = {dashboard:{...this.viewData.dashboard,...this.viewData},widgets:null};
this.dashboard=data.dashboard||{};
const equipment = document.body.clientWidth;
const ratioEquipment = equipment / data.dashboard.width;
this.bigScreenStyle = {
width: data.dashboard.width + "px",
height: data.dashboard.height + "px",
"background-color": data.dashboard.backgroundColor,
"background-image": "url(" + data.dashboard.backgroundImage + ")",
"background-position": "0% 0%",
"background-size": "100% 100%",
"background-repeat": "initial",
"background-attachment": "initial",
"background-origin": "initial",
"background-clip": "initial",
transform: `scale(${ratioEquipment}, ${ratioEquipment})`,
"transform-origin": "0 0"
};
data.dashboard.widgets.forEach((item, index) => {
item.value.widgetId = item.value.setup.widgetId
item.value.widgetCode = item.value.setup.widgetCode
if (item.value.setup.componentLinkage && item.value.setup.componentLinkage.length) {
this.$store.commit('SET_ALL_COMPONENT_LINKAGE', {
index,
widgetId: item.value.widgetId,
linkageArr: item.value.setup.componentLinkage
})
}
})
this.widgets = data.dashboard.widgets;
// 定时刷新
if(data.dashboard.refreshSeconds>0) {
setTimeout(function(){ window.location.reload(); }, data.dashboard.refreshSeconds*1000);
}
},
Debounce:(fn,t)=>{
const delay=t||500;
let timer=null;
return(...args)=>{
if(timer){
clearTimeout(timer);
}
const context=this
timer=setTimeout(()=>{
fn.apply(context,args);
},delay);
}
},
setScale(){
const scale=this.getScale();
this.bigScreenStyle.transform='scale('+scale.scalex+','+scale.scaley+')'
},
getScale(){
let width = this.dashboard.width
let height = this.dashboard.height
// 固定宽度比例
const scalex = window.innerWidth / width
// 高度按相同比例缩放,但允许超出屏幕高度
const scaley = scalex
return {
scalex,
scaley
}
},
}
};
</script>
<style lang="scss">
.layout {
width: 100%;
text-align: center;
}
.drillDrownViewLayout{
min-height: calc(100vh - 34px) !important;
overflow-y:auto !important;
&::-webkit-scrollbar {
display: none; /* 隐藏滚动条 */
}
}
.bottom-text {
width: 100%;
color: #a0a0a0;
position: fixed;
bottom: 16px;
z-index: 9999;
}
.drillDrownViewLayout{
}
</style>

View File

@@ -14,24 +14,39 @@
v-model="widget.value"
:index="index"
:type="widget.type"
@oepnTheDrillView="oepnTheDrillView"
/>
</div>
<el-dialog
:title="''"
class="dialogDrillDrown dialogDrillDrownView" :visible.sync="visiableDrillView">
<drill-drown-view :screenData="screenData"></drill-drown-view>
</el-dialog>
</div>
</template>
<script>
import widget from "../designer/widget/temp";
import DrillDrownView from "./drillDrownView";
import { detailDashboard } from "@/api/bigscreen";
export default {
name: "Login",
components: {
widget
widget,
DrillDrownView
},
data() {
return {
bigScreenStyle: {},
dashboard:{},
widgets: []
widgets: [],
screenData:{
dashboard: { },
widgets: []
},
visiableDrillView:false
};
},
mounted() {
@@ -39,6 +54,10 @@ export default {
window.onresize=this.Debounce(this.setScale,500);
},
methods: {
oepnTheDrillView(val){
this.screenData=val
this.visiableDrillView=true
},
async getData() {
const reportCode = this.$route.query.reportCode;
const { code, data } = await detailDashboard(reportCode);
@@ -114,8 +133,24 @@ export default {
<style lang="scss">
.layout {
width: 100%;
height: 100%;
text-align: center;
}
.dialogDrillDrownView .el-dialog{
width:auto;
margin-top:0 !important;
.el-dialog__body{
padding:0;
max-height:calc(100vh - 34px);
overflow:auto;
&::-webkit-scrollbar {
display: none; /* 隐藏滚动条 */
}
}
.el-dialog__header .el-dialog__headerbtn{
top: 6px;
}
}
.bottom-text {
width: 100%;

View File

@@ -82,6 +82,8 @@ import Hamburger from "@/components/Hamburger";
import { getStorageItem } from "@/utils/storage";
import { reqUpdatePassword } from "@/api/login";
import { transPsw } from "@/utils/encrypted";
import {getAccessUser} from "@/utils/auth";
import {outlogcas} from "@/api/login";
export default {
data() {
@@ -156,11 +158,34 @@ export default {
cancelButtonText: "取消",
type: "warning"
}).then(() => {
sessionStorage.clear();
localStorage.clear();
this.$router.push("/login");
debugger
//获取登录人
let user = getAccessUser()
console.log(user.loginName)
debugger
this.outlogincasapi();
debugger
console.log(user.loginName)
// sessionStorage.clear();
// localStorage.clear();
// this.$router.push("/login");
});
},
async outlogincasapi() {
debugger
const obj = {
loginName: "admin",
password: "demo",
verifyCode: "",
};
const { code, data } = await outlogcas(obj);
sessionStorage.clear();
localStorage.clear();
this.$router.push("/login");
},
// 修改密码
updatePassword() {
this.wordVisible = true;

View File

@@ -1,7 +1,5 @@
<template>
<div class="login_container">
<div class="login_contant">
<img src="@/assets/images/login.jpg" alt="image" class="login_img" />
<el-form
@@ -13,28 +11,25 @@
label-position="left"
@keyup.enter.native="handleLogin"
>
<div class="title_container">
智慧大屏
</div>
<div style="height: 30px;"></div>
<div class="title_container">国瑞药业驾驶舱平台</div>
<div style="height: 30px"></div>
<div class="form_fields">
<el-form-item prop="loginName">
<el-input
ref="loginName"
v-model="loginForm.loginName"
placeholder="用户名"
name="loginName"
type="text"
tabindex="1"
autocomplete="on"
@focus="setTop('0')"
@change="getPsw"
/>
</el-form-item>
<el-form-item prop="loginName">
<el-input
ref="loginName"
v-model="loginForm.loginName"
placeholder="用户名"
name="loginName"
type="text"
tabindex="1"
autocomplete="on"
@focus="setTop('0')"
@change="getPsw"
/>
</el-form-item>
<div>
<div style="height: 20px;"></div>
<div style="height: 20px"></div>
<input
name="password"
type="password"
@@ -62,7 +57,7 @@
@keyup.native="checkCapslock"
/>
<span class="show_pwd" @click="showPwd">
<div style="height: 10px;"></div>
<div style="height: 10px"></div>
<i class="el-icon-view" />
</span>
</el-form-item>
@@ -75,13 +70,13 @@
<p>记住密码</p>
</div>
</div>
<div style="height: 30px;"></div>
<div style="height: 30px"></div>
<el-button
:loading="loading"
type="primary"
class="login_btn"
@click.native.prevent="handleLogin"
> </el-button
> </el-button
>
</el-form>
</div>
@@ -100,13 +95,14 @@
import Verify from "@/components/verifition/Verify";
import cookies from "js-cookie";
import { Decrypt, Encrypt } from "@/utils/index";
import { login } from "@/api/login";
import { login, logincas } from "@/api/login";
import { transPsw } from "@/utils/encrypted";
import { setToken, setAccessUser } from "@/utils/auth";
import { setToken, getToken, setAccessUser } from "@/utils/auth";
export default {
name: "Login",
components: {
Verify
Verify,
},
data() {
return {
@@ -115,11 +111,13 @@ export default {
loginForm: {
loginName: "",
password: "",
verifyCode: ""
verifyCode: "",
},
loginRules: {
loginName: [{ required: true, message: "用户名必填", trigger: "blur" }],
password: [{ required: true, message: "用户密码必填", trigger: "blur" }]
password: [
{ required: true, message: "用户密码必填", trigger: "blur" },
],
},
passwordType: "password",
capsTooltip: false,
@@ -127,25 +125,45 @@ export default {
redirect: undefined,
otherQuery: {},
needCaptcha: false,
centerDialogVisible: false
centerDialogVisible: false,
};
},
watch: {
$route: {
// 监听路由获取上个路由from的地址和参数
handler: function(route) {
handler: function (route) {
const query = route.query;
if (query) {
this.redirect = query.redirect;
this.otherQuery = this.getOtherQuery(query);
}
},
immediate: true
}
immediate: true,
},
},
mounted() {
this.handleLoginFocus();
//获取url传参 ticket url
const ticket = this.$route.query.ticket;
let token = getToken();
console.log(ticket);
console.log(token);
if (ticket == undefined && token == null) {
//跳转统一身份认证
var isTs = true;
if (isTs) {
window.location.href = 'http://192.168.1.241/login?redirect=/lig/oauth2/oauth2/application&appid=330b4ecb60c9a6802b957fe1e5a5ecd3&url=http%3A%2F%2F192.168.1.241%3A8080%2F%23%2Flogin'+(this.redirect?'&ssUrl='+this.redirect:'');
// window.location.href = 'http://localhost/login?redirect=/lig/oauth2/oauth2/application&appid=330b4ecb60c9a6802b957fe1e5a5ecd3&url=http%3A%2F%2Flocalhost%3A9528%2F%23%2Flogin'+(this.redirect?'&ssUrl='+this.redirect:'');
}
} else if (ticket != undefined && token == null) {
//请求登录
this.logincasapi();
} else {
}
},
created() {},
methods: {
handleLoginFocus() {
if (this.loginForm.loginName === "") {
@@ -181,7 +199,7 @@ export default {
},
// 滑动验证码
useVerify() {
this.$refs.loginForm.validate(valid => {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.$refs.verify.show();
} else {
@@ -198,7 +216,7 @@ export default {
},
// 登录操作
handleLogin() {
this.$refs.loginForm.validate(valid => {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.loading = true;
// 登录失败次数过多需要展示滑动验证码
@@ -212,11 +230,41 @@ export default {
}
});
},
async logincasapi() {
const ticket = this.$route.query.ticket;
const obj = {
loginName: ticket,
password: "demo",
verifyCode: "",
};
const { code, data } = await logincas(obj);
this.loading = false;
if (code != "200") return;
setToken(data.token);
setAccessUser(data);
debugger
// 选中记住密码时 把密码存到cookie里,时效15天
this.rememberPsw &&
cookies.set(
`u_${this.loginForm.loginName}`,
Encrypt(this.loginForm.password),
{ expires: 15 }
);
if (data && data.captcha) {
this.needCaptcha = true;
} else {
this.needCaptcha = false;
this.$router.push({
path: this.redirect || "/index",
query: this.otherQuery,
});
}
},
async loginApi() {
const obj = {
loginName: this.loginForm.loginName,
password: transPsw(this.loginForm.password),
verifyCode: ""
verifyCode: "",
};
const { code, data } = await login(obj);
this.loading = false;
@@ -236,7 +284,7 @@ export default {
this.needCaptcha = false;
this.$router.push({
path: this.redirect || "/index",
query: this.otherQuery
query: this.otherQuery,
});
}
},
@@ -247,8 +295,8 @@ export default {
}
return acc;
}, {});
}
}
},
},
};
</script>
@@ -260,15 +308,14 @@ export default {
opacity: 10;
background: #fff;
}
.delete {
color: #fff;
}
.delete {
color: #fff;
}
.login_container {
.el-input {
display: inline-block;
width: 100%;
}
.el-form-item {
@@ -345,7 +392,7 @@ export default {
position: relative;
width: 100%;
//height: calc(100% - 60px);
height: 100%;
height: 100%;
.login_img {
display: block;
width: 100%;
@@ -359,22 +406,21 @@ export default {
min-width: 460px;
width: 22%;
height: 400px;
background-color: #006DD9;
background-color: #006dd9;
opacity: 0.6;
padding: 30px;
overflow: hidden;
.title_container {
position: relative;
font-size: 22px;
color: #fff;
text-align: center;
color: #fff;
text-align: center;
}
.form_fields {
position: relative;
width: 100%;
overflow: hidden;
.show_pwd {
position: absolute;
right: 10px;
@@ -427,7 +473,7 @@ export default {
.login_btn {
min-width: 400px;
height: 40px;
background: #0BA1F8;
background: #0ba1f8;
border: none;
// border-radius: 10px;
font-size: 20px;