
都江堰市经科信局工业企业互联网平台
项目介绍 Link to 项目介绍
- 本系统主要有后台管理系统、H5、小程序、大屏等项目,主要是对企业的信息进行管理,包括企业信息、产品信息、服务信息、商品信息的管理和展示。主要包含以下功能:
大屏
门户管理
栏目管理
内容管理
使用帮助
友情链接
页面Banner
企业管理
- 企业基本信息 1.1 企业介绍 1.2 产品中心 1.3 部门配置 1.4 人员配置
- 安全环保 2.1 安环资质 2.2 自查记录 2.3 巡检记录 2.4 整改记录 2.5 企业考核评价 2.6 企业风控 2.7 企业运营指标
- 业务办理 3.1 企业需求审核 3.2 项目申报审核 3.3 企业巡检 3.4 意见处置 3.5 发布公告 3.6 业务部门
- 企业诉求 4.1 需求发布 4.2 意见反馈 4.3 项目申报
系统管理
- 统计分析
- 调查问卷
- 标签管理
项目难点 Link to 项目难点
- 自定义富文本编辑器(附件上传、多功能表格、表情、地图、秀米等插件接入)
- leaflet离线地图绘制自定义图层
- 后台配置门户栏目、内容、banner等信息实现高度可配置化
- 单点登录【部署在同一个服务器上,域名协议相同端口号不同但共享cookie】
代码示例 Link to 代码示例
VUE
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
<!-- 基于UEditor封装的UEditorPlus富文本组件 -->
<template>
<div>
<div ref="container" :name="name"></div>
</div>
</template>
<script>
import LoadEvent from "@/utils/ueditor/Event.js";
import debounce from "@/utils/ueditor/debounce.js";
import asyncSeries from "@/utils/ueditor/async-series.js";
import randomString from "@/utils/ueditor/randomString.js";
const STATUS_MAP = {
UN_READY: "UN_READY", // 尚未初始化
PENDING: "PENDING", // 开始初始化但尚未 ready
READY: "READY", // 初始化完成并已 ready
};
export default {
name: "VueUeditorWrap",
data() {
return {
status: STATUS_MAP.UN_READY,
defaultConfig: {
// VUE CLI 3 会添加 process.env.BASE_URL 的环境变量,而 VUE CLI 2 没有,所以借此设置 UEDITOR_HOME_URL,能涵盖大部分 Vue 开发者的使用场景
UEDITOR_HOME_URL:
typeof process !== "undefined" && process.env.BASE_URL
? process.env.BASE_URL + "UEditor/"
: "/static/UEditor/",
},
};
},
props: {
// v-model 实现方式
mode: {
type: String,
default: "observer",
validator: function (value) {
// 1. observer 借助 MutationObserver API https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver
// 2. listener 借助 UEditor 的 contentChange 事件 https://ueditor.baidu.com/doc/#UE.Editor:contentChange
return ["observer", "listener"].indexOf(value) !== -1;
},
},
value: {
type: String,
default: "",
},
config: {
type: Object,
default: function () {
return {};
},
},
init: {
type: Function,
default: () => {},
},
destroy: {
type: Boolean,
default: true,
},
name: {
type: String,
default: "",
},
observerDebounceTime: {
type: Number,
default: 50,
validator: function (value) {
return value >= 20;
},
},
observerOptions: {
type: Object,
default: function () {
// https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit
return {
attributes: true, // 是否监听 DOM 元素的属性变化
attributeFilter: ["src", "style", "type", "name"], // 只有在该数组中的属性值的变化才会监听
characterData: true, // 是否监听文本节点
childList: true, // 是否监听子节点
subtree: true, // 是否监听后代元素
};
},
},
// 本组件提供对普通 Vue 项目和 Nuxt 项目开箱即用的支持,但如果是自己搭建的 Vue SSR 项目,可能需要自行区分是客户端还是服务端环境并跳过环境检测,直接初始化
forceInit: {
type: Boolean,
default: false,
},
// 手动设置 UEditor ID
editorId: {
type: String,
},
// 指定 UEditor 依赖的静态资源,js & css
editorDependencies: Array,
// 检测依赖的静态资源是否加载完成的方法
editorDependenciesChecker: Function,
},
computed: {
mixedConfig() {
return {
...this.defaultConfig,
...this.config,
};
},
},
methods: {
// 添加自定义按钮(自定义按钮,自定义弹窗等操作从 2.2.0 版本开始不再考虑直接集成,这会使得组件和 UEditor 过度耦合,但为了兼容一些老版用户的写法,这个方法依然保留)
registerButton({ name, icon, tip, handler, index, UE = window.UE }) {
UE.registerUI(
name,
(editor, name) => {
editor.registerCommand(name, {
execCommand: () => {
handler(editor, name);
},
});
const btn = new UE.ui.Button({
name,
title: tip,
cssRules: `background-image: url(${icon}) !important;background-size: cover;`,
onclick() {
editor.execCommand(name);
},
});
editor.addListener("selectionchange", () => {
const state = editor.queryCommandState(name);
if (state === -1) {
btn.setDisabled(true);
btn.setChecked(false);
} else {
btn.setDisabled(false);
btn.setChecked(state);
}
});
return btn;
},
index,
this.id
);
},
// 实例化编辑器
_initEditor() {
this.$refs.container.id = this.id =
this.editorId || "editor_" + randomString(8); // 这么做是为了支持 Vue SSR,因为如果把 id 属性放在 data 里会导致服务端和客户端分别计算该属性的值,而造成 id 不匹配无法初始化的 BUG
this.init();
this.$emit("before-init", this.id, this.mixedConfig);
this.$emit("beforeInit", this.id, this.mixedConfig); // 虽然这个驼峰的写法会导致使用 DOM 模版时出现监听事件自动转小写的 BUG,但如果经过编译的话并不会有这个问题,为了兼容历史版本,不做删除,参考 https://vuejs.org/v2/guide/components-custom-events.html#Event-Names
this.editor = window.UE.getEditor(this.id, this.mixedConfig);
this.editor.addListener("ready", () => {
if (this.status === STATUS_MAP.READY) {
// 使用 keep-alive 组件会出现这种情况
this.editor.setContent(this.value);
} else {
this.status = STATUS_MAP.READY;
this.$emit("ready", this.editor);
if (this.value) {
this.editor.setContent(this.value);
}
}
// console.log('ueditor-wrap', this.mode)
if (this.mode === "observer" && window.MutationObserver) {
this._observerChangeListener();
} else {
this._normalChangeListener();
}
});
},
// 动态创建 script 标签来加载 JS 脚本,保证同一个脚本只被加载一次
_loadScript(link) {
return new Promise((resolve, reject) => {
window.$loadEventBus.on(link, resolve);
if (window.$loadEventBus.listeners[link].requested === false) {
window.$loadEventBus.listeners[link].requested = true;
// 如果这个资源从未被请求过,就手动创建脚本去加载
const script = document.createElement("script");
script.src = link;
script.onload = () => {
window.$loadEventBus.emit(link);
};
script.onerror = reject;
document.getElementsByTagName("head")[0].appendChild(script);
}
});
},
// 动态创建 link 标签来加载 CSS 文件
_loadCss(link) {
return new Promise((resolve, reject) => {
window.$loadEventBus.on(link, resolve);
if (window.$loadEventBus.listeners[link].requested === false) {
window.$loadEventBus.listeners[link].requested = true;
const css = document.createElement("link");
css.type = "text/css";
css.rel = "stylesheet";
css.href = link;
css.onload = () => {
window.$loadEventBus.emit(link);
};
css.onerror = reject;
document.getElementsByTagName("head")[0].appendChild(css);
}
});
},
// 加载 UEditor 相关的静态资源
_loadEditorDependencies() {
// 创建加载资源的事件通信载体
if (!window.$loadEventBus) {
window.$loadEventBus = new LoadEvent();
}
// 默认要加载的资源
const defaultEditorDependencies = [
"ueditor.config.js",
"ueditor.all.min.js",
];
// 判断上面的默认资源是否已经加载过的校验函数
const defaultEditorDependenciesChecker = () => {
// 判断 ueditor.config.js 和 ueditor.all.js 是否均已加载
// 仅加载完ueditor.config.js时UE对象和UEDITOR_CONFIG对象存在,仅加载完ueditor.all.js时UEDITOR_CONFIG对象存在,但为空对象
return (
window.UE &&
window.UE.getEditor &&
window.UEDITOR_CONFIG &&
Object.keys(window.UEDITOR_CONFIG).length !== 0
);
};
return new Promise((resolve, reject) => {
if (
this.editorDependencies &&
this.editorDependenciesChecker &&
this.editorDependenciesChecker()
) {
resolve();
return;
}
if (!this.editorDependencies && defaultEditorDependenciesChecker()) {
resolve();
return;
}
// 把 js 和 css 分组
const { jsLinks, cssLinks } = (
this.editorDependencies || defaultEditorDependencies
).reduce(
(res, link) => {
// 如果不是完整的 URL 就在前面补上 UEDITOR_HOME_URL, 完整的 URL 形如:
// 1. http://www.example.com/xxx.js
// 2. https://www.example.com/xxx.js
// 3. //www.example.com/xxx.js
// 4. www.example.com/xxx.js
const isFullUrl =
/^((https?:)?\/\/)?[-a-zA-Z0-9]+(\.[-a-zA-Z0-9]+)+\//.test(link);
if (!isFullUrl) {
link = (this.mixedConfig.UEDITOR_HOME_URL || "") + link;
}
if (link.slice(-3) === ".js") {
res.jsLinks.push(link);
} else if (link.slice(-4) === ".css") {
res.cssLinks.push(link);
}
return res;
},
{
jsLinks: [],
cssLinks: [],
}
);
Promise.all([
Promise.all(cssLinks.map((link) => this._loadCss(link))),
// 依次加载依赖的 JS 文件,JS 执行是有顺序要求的,比如 ueditor.all.js 就要晚于 ueditor.config.js 执行
// 动态创建 script 是先加载完的先执行,所以不可以一次性创建所有资源的引入脚本
asyncSeries(jsLinks.map((link) => () => this._loadScript(link))),
])
.then(() => resolve())
.catch(reject);
});
},
_contentChangeHandler() {
// console.log('contentChange')
this.innerValue = this.editor.getContent();
this.$emit("input", this.innerValue);
},
// 基于 UEditor 的 contentChange 事件
_normalChangeListener() {
this.editor.addListener("contentChange", this._contentChangeHandler);
},
// 基于 MutationObserver API
_observerChangeListener() {
const changeHandle = () => {
if (this.editor.document.getElementById("baidu_pastebin")) {
return;
}
this.innerValue = this.editor.getContent();
this.$emit("input", this.innerValue);
};
// 函数防抖
this.observer = new MutationObserver(
debounce(changeHandle, this.observerDebounceTime)
);
this.observer.observe(this.editor.body, this.observerOptions);
},
},
deactivated() {
if (this.editor) {
this.editor.removeListener("contentChange", this._contentChangeHandler);
}
if (this.observer) {
this.observer.disconnect();
}
},
beforeDestroy() {
if (this.destroy && this.editor && this.editor.destroy) {
this.editor.destroy();
}
if (this.observer && this.observer.disconnect) {
this.observer.disconnect();
}
},
// v-model语法糖实现
watch: {
value: {
handler(value) {
if (this.status === STATUS_MAP.UN_READY) {
this.status = STATUS_MAP.PENDING;
(this.forceInit || typeof window !== "undefined") &&
this._loadEditorDependencies()
.then(() => {
this.$refs.container
? this._initEditor()
: this.$nextTick(() => this._initEditor());
})
.catch(() => {
throw new Error(
"[vue-ueditor-wrap] UEditor 资源加载失败!请检查资源是否存在,UEDITOR_HOME_URL 是否配置正确!"
);
});
} else if (this.status === STATUS_MAP.READY) {
value === this.innerValue || this.editor.setContent(value || "");
}
},
immediate: true,
},
},
};
</script>
都江堰市经科信局工业企业互联网平台
Copyright © 毛杭飞 2025 - All Rights Reserved