refactor: 四端完成 Options API → script setup + vuex → pinia 迁移

- buyer(95) / seller(169) / im(53) / manager(280) 共 597 个组件全部从 Options API 转换为 <script setup>
- 四端 vuex 全部移除,状态管理统一迁移至 pinia 4(stores/ 目录)
- util.js 等基础设施去除 vm 首参依赖;全局属性($Message/$filters 等)在 script 内改为具名导入
- 被外部 ref 访问的公共组件均已补齐 defineExpose(清单见各端 MIGRATION-VUE3.md)
- 各端 eslint 启用 vue/component-api-style 防止新代码回退 Options API
- 四端 yarn build 全部通过;迁移约定与注意事项已更新至各端 MIGRATION-VUE3.md
This commit is contained in:
lifenlong
2026-10-13 07:06:59 +08:00
parent 870bacdd8c
commit 88fc1b0b2a
674 changed files with 71220 additions and 69943 deletions

View File

@@ -8,132 +8,142 @@
</div>
</div>
</template>
<script>
<script setup>
import {
computed,
nextTick,
onActivated,
onDeactivated,
onMounted,
onUnmounted,
ref,
watch,
} from "vue";
import { initEditor } from "@/components/editor/config";
import uploadImage from "@/components/editor/upload-image.vue";
export default {
components:{uploadImage},
name: "Tinymce",
props: {
modelValue: {
type: String,
default: "",
},
value: {
type: String,
default: "",
},
height:{
type:String,
default:'500px'
}
},
computed: {
editorContent() {
return this.modelValue ?? this.value ?? "";
},
},
data() {
return {
// 引入编辑器的配置
initEditor,
hasChange: false, // 标记内容是否有更改
hasInit: false, // 标记编辑器是否已初始化
tinymceId:
"tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + ""), // 生成唯一的编辑器 ID
fullscreen: false, // 标记编辑器是否处于全屏模式
toolbar: [], // 工具栏配置
content: "", // 编辑器内容
};
},
created() {
this.init();
},
watch: {
editorContent: {
handler(val) {
if (!this.hasChange && this.hasInit) {
// 当内容有更改且编辑器已初始化时,更新编辑器的内容
this.$nextTick(() => {
const editor = window.tinymce?.get(this.tinymceId);
if (editor) editor.setContent(val || "");
});
}
},
},
},
methods: {
emitContent(content) {
this.$emit("update:modelValue", content);
this.$emit("input", content);
},
// 数据返回并给富文本框插入图片
insertImage(arr){
arr.forEach(v => window.tinymce.get(this.tinymceId).insertContent(`<img src="${v}" >`))
},
init() {
// 初始化编辑器
this.initTinymce();
},
initTinymce() {
const _this = this;
window.tinymce.init({
selector: `#${this.tinymceId}`,
convert_urls: false,
init_instance_callback: (editor) => {
if (_this.editorContent) {
// 如果有初始值,则设置编辑器的内容为初始值
_this.$nextTick(() => editor.setContent(_this.editorContent));
}
_this.hasInit = true;
// 监听编辑器内容的变化
editor.on("NodeChange Change KeyUp SetContent", () => {
_this.hasChange = true;
// Vue3 v-model 使用 update:modelValue,同时保留 input 兼容旧用法
_this.emitContent(editor.getContent());
});
},
setup(editor) {
// 监听全屏状态变化
editor.on("FullscreenStateChanged", (e) => {
_this.fullscreen = e.state;
});
},
..._this.initEditor,
height:this.height
defineOptions({ name: "Tinymce" });
const props = defineProps({
modelValue: {
type: String,
default: "",
},
value: {
type: String,
default: "",
},
height: {
type: String,
default: "500px",
},
});
const emit = defineEmits(["update:modelValue", "input"]);
// 引入编辑器的配置(data 中的 initEditor 与 import 同名,直接使用 import)
const hasChange = ref(false); // 标记内容是否有更改
const hasInit = ref(false); // 标记编辑器是否已初始化
const tinymceId =
"tinymce-" + +new Date() + ((Math.random() * 1000).toFixed(0) + ""); // 生成唯一的编辑器 ID
const fullscreen = ref(false); // 标记编辑器是否处于全屏模式
const toolbar = ref([]); // 工具栏配置
const content = ref(""); // 编辑器内容
const editorContent = computed(() => {
return props.modelValue ?? props.value ?? "";
});
watch(editorContent, (val) => {
if (!hasChange.value && hasInit.value) {
// 当内容有更改且编辑器已初始化时,更新编辑器的内容
nextTick(() => {
const editor = window.tinymce?.get(tinymceId);
if (editor) editor.setContent(val || "");
});
}
});
function emitContent(content) {
emit("update:modelValue", content);
emit("input", content);
}
// 数据返回并给富文本框插入图片
function insertImage(arr) {
arr.forEach((v) =>
window.tinymce.get(tinymceId).insertContent(`<img src="${v}" >`)
);
}
function init() {
// 初始化编辑器
initTinymce();
}
function initTinymce() {
window.tinymce.init({
selector: `#${tinymceId}`,
convert_urls: false,
init_instance_callback: (editor) => {
if (editorContent.value) {
// 如果有初始值,则设置编辑器的内容为初始值
nextTick(() => editor.setContent(editorContent.value));
}
hasInit.value = true;
// 监听编辑器内容的变化
editor.on("NodeChange Change KeyUp SetContent", () => {
hasChange.value = true;
// Vue3 v-model 使用 update:modelValue,同时保留 input 兼容旧用法
emitContent(editor.getContent());
});
},
setContent(value) {
// 设置编辑器的内容
window.tinymce.get(this.tinymceId).setContent(value);
setup(editor) {
// 监听全屏状态变化
editor.on("FullscreenStateChanged", (e) => {
fullscreen.value = e.state;
});
},
getContent() {
// 获取编辑器的内容
return window.tinymce.get(this.tinymceId).getContent();
},
destroyTinymce() {
const tinymce = window.tinymce.get(this.tinymceId);
...initEditor,
if (tinymce) {
// 销毁编辑器实例
tinymce.destroy();
}
},
},
mounted() {
this.init();
},
activated() {
if (window.tinymce) {
this.initTinymce();
}
},
deactivated() {
this.destroyTinymce();
},
unmounted() {
this.destroyTinymce();
},
};
height: props.height,
});
}
function getEditor() {
// 获取 tinymce 实例(原组件无此方法,为对齐 seller 版暴露能力而新增)
return window.tinymce?.get(tinymceId);
}
function setContent(value) {
// 设置编辑器的内容
window.tinymce.get(tinymceId).setContent(value);
}
function getContent() {
// 获取编辑器的内容
return window.tinymce.get(tinymceId).getContent();
}
function destroyTinymce() {
const tinymce = window.tinymce.get(tinymceId);
if (tinymce) {
// 销毁编辑器实例
tinymce.destroy();
}
}
// created 钩子:顶层直接执行(保留原 created + mounted 双重 init 的历史行为)
init();
onMounted(() => {
init();
});
onActivated(() => {
if (window.tinymce) {
initTinymce();
}
});
onDeactivated(() => {
destroyTinymce();
});
onUnmounted(() => {
destroyTinymce();
});
// 父组件通过 ref 调用:articleList 使用 getContent;setContent/getEditor 对齐 seller 版暴露能力
defineExpose({ getEditor, setContent, getContent });
</script>