49 lines
1.6 KiB
TypeScript
49 lines
1.6 KiB
TypeScript
/**
|
||
* {yz:xxx} 站点标签指令(dedecms 风格)
|
||
*
|
||
* 用法:在模板里直接写标签,元素加 v-yz 即可
|
||
* <p v-yz>{yz:icp}</p>
|
||
* <p v-yz>备案号:{yz:icp} | {yz:policeicp}</p>
|
||
*
|
||
* 元素内所有 {yz:标签名} 会被替换为「官网管理 - 站点设置」中对应的值,
|
||
* 未配置的标签输出空字符串(与后端 themes 模板行为一致)。
|
||
*
|
||
* 注意:指令通过 innerHTML 替换内容,因此元素内只能放纯文本/HTML 片段,
|
||
* 不要放子组件或需要响应式绑定的内容;需要条件渲染的地方请用 siteInfo.xxx。
|
||
*/
|
||
import { watchEffect, type Directive } from "vue";
|
||
import { siteTags } from "@/config/site";
|
||
|
||
/** 与后端 tagengine 的全局标签正则保持一致 */
|
||
const TAG_PATTERN = /\{yz:([a-zA-Z_]\w*)\}/g;
|
||
|
||
/** 每个元素对应的 watchEffect 停止函数 */
|
||
const stops = new WeakMap<HTMLElement, () => void>();
|
||
|
||
/** 依据当前标签值渲染元素内容;始终基于首次保存的原文,避免重复替换 */
|
||
function render(el: HTMLElement): void {
|
||
if (!el.dataset.yzRaw) {
|
||
el.dataset.yzRaw = el.innerHTML;
|
||
}
|
||
const raw = el.dataset.yzRaw;
|
||
|
||
el.innerHTML = raw.replace(
|
||
TAG_PATTERN,
|
||
(_match: string, key: string) => siteTags[key] ?? ""
|
||
);
|
||
}
|
||
|
||
export const yz: Directive<HTMLElement> = {
|
||
mounted(el) {
|
||
// watchEffect 会追踪 render 中读取到的标签值;
|
||
// 站点设置是异步拉取的,数据到位后会自动重新渲染
|
||
stops.set(el, watchEffect(() => render(el)));
|
||
},
|
||
unmounted(el) {
|
||
stops.get(el)?.();
|
||
stops.delete(el);
|
||
},
|
||
};
|
||
|
||
export default yz;
|