mirror of
https://gitee.com/newgateway/vtj.git
synced 2026-06-06 20:49:54 +08:00
feat: ✨ parser style
This commit is contained in:
@@ -34,7 +34,8 @@
|
|||||||
"@vtj/coder": "workspace:~",
|
"@vtj/coder": "workspace:~",
|
||||||
"@vtj/core": "workspace:~",
|
"@vtj/core": "workspace:~",
|
||||||
"@vue/compiler-dom": "~3.5.13",
|
"@vue/compiler-dom": "~3.5.13",
|
||||||
"@vue/compiler-sfc": "~3.5.13"
|
"@vue/compiler-sfc": "~3.5.13",
|
||||||
|
"postcss": "~8.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vtj/cli": "workspace:~"
|
"@vtj/cli": "workspace:~"
|
||||||
|
|||||||
@@ -1,22 +1,43 @@
|
|||||||
import { type BlockSchema, type NodeSchema, BlockModel } from '@vtj/core';
|
import {
|
||||||
|
type BlockSchema,
|
||||||
|
type NodeSchema,
|
||||||
|
type Dependencie,
|
||||||
|
BlockModel
|
||||||
|
} from '@vtj/core';
|
||||||
import { tsFormatter } from '@vtj/coder';
|
import { tsFormatter } from '@vtj/coder';
|
||||||
import { parseSFC, isJSCode } from '../shared';
|
import { parseSFC, isJSCode } from '../shared';
|
||||||
import { parseTemplate } from './template';
|
import { parseTemplate } from './template';
|
||||||
import { parseScripts } from './scripts';
|
import { parseScripts, type ImportStatement } from './scripts';
|
||||||
|
import { parseStyle } from './style';
|
||||||
import { patchCode } from './utils';
|
import { patchCode } from './utils';
|
||||||
|
|
||||||
export interface ParseVueOptions {
|
export interface ParseVueOptions {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
source: string;
|
source: string;
|
||||||
|
dependencies?: Dependencie[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function parseVue(options: ParseVueOptions) {
|
export async function parseVue(options: ParseVueOptions) {
|
||||||
const { id, name, source } = options;
|
const { id, name, source, dependencies = [] } = options;
|
||||||
const sfc = parseSFC(source);
|
const sfc = parseSFC(source);
|
||||||
const { state, watch, lifeCycles, computed, methods, props, emits, inject } =
|
const { styles, css } = parseStyle(sfc.styles.join('\n'));
|
||||||
parseScripts(sfc.script);
|
const {
|
||||||
const { nodes, slots, context } = parseTemplate(id, name, sfc.template);
|
state,
|
||||||
|
watch,
|
||||||
|
lifeCycles,
|
||||||
|
computed,
|
||||||
|
methods,
|
||||||
|
props,
|
||||||
|
emits,
|
||||||
|
inject,
|
||||||
|
handlers,
|
||||||
|
imports
|
||||||
|
} = parseScripts(sfc.script);
|
||||||
|
const { nodes, slots, context } = parseTemplate(id, name, sfc.template, {
|
||||||
|
handlers,
|
||||||
|
styles
|
||||||
|
});
|
||||||
const dsl: BlockSchema = {
|
const dsl: BlockSchema = {
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
@@ -29,16 +50,19 @@ export async function parseVue(options: ParseVueOptions) {
|
|||||||
methods,
|
methods,
|
||||||
slots,
|
slots,
|
||||||
emits,
|
emits,
|
||||||
nodes
|
nodes,
|
||||||
|
css
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const computedKeys = Object.keys(computed || {});
|
||||||
|
const { libs } = parseDeps(imports, dependencies);
|
||||||
await walkDsl(dsl, async (node: NodeSchema) => {
|
await walkDsl(dsl, async (node: NodeSchema) => {
|
||||||
await walkNode(node, async (content: any) => {
|
await walkNode(node, async (content: any) => {
|
||||||
if (isJSCode(content)) {
|
if (isJSCode(content)) {
|
||||||
const options = {
|
const options = {
|
||||||
context,
|
context,
|
||||||
computed: [],
|
computed: computedKeys,
|
||||||
libs: {}
|
libs
|
||||||
};
|
};
|
||||||
const code = await tsFormatter(content.value);
|
const code = await tsFormatter(content.value);
|
||||||
content.value = patchCode(code, node.id as string, options);
|
content.value = patchCode(code, node.id as string, options);
|
||||||
@@ -88,3 +112,28 @@ async function walkNode(node: NodeSchema, callback: (n: any) => Promise<void>) {
|
|||||||
};
|
};
|
||||||
await walking(node);
|
await walking(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseDeps(
|
||||||
|
imports: ImportStatement[] = [],
|
||||||
|
dependencies: Dependencie[] = []
|
||||||
|
) {
|
||||||
|
const libs: Record<string, string> = {};
|
||||||
|
const depsMap = dependencies.reduce(
|
||||||
|
(prev, current) => {
|
||||||
|
prev[current.package] = current.library;
|
||||||
|
return prev;
|
||||||
|
},
|
||||||
|
{} as Record<string, string>
|
||||||
|
);
|
||||||
|
for (const { from, imports: names } of imports) {
|
||||||
|
names.forEach((name) => {
|
||||||
|
const library = depsMap[from];
|
||||||
|
if (library) {
|
||||||
|
libs[name] = library;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
libs
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
33
packages/parser/src/vue/style.ts
Normal file
33
packages/parser/src/vue/style.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import postcss from 'postcss';
|
||||||
|
|
||||||
|
export type CSSRules = Record<string, Record<string, string>>;
|
||||||
|
|
||||||
|
export interface ParseStyleResult {
|
||||||
|
styles: CSSRules;
|
||||||
|
css: string;
|
||||||
|
}
|
||||||
|
export function parseStyle(content: string) {
|
||||||
|
const styles: CSSRules = {};
|
||||||
|
const css: string[] = [];
|
||||||
|
const root = postcss.parse(content);
|
||||||
|
const classRegex = /^.[\w]+_[\w]{5,}/;
|
||||||
|
for (const rule of root.nodes) {
|
||||||
|
if (rule.type === 'rule') {
|
||||||
|
const style: Record<string, string> = {};
|
||||||
|
if (classRegex.test(rule.selector)) {
|
||||||
|
rule.nodes.forEach((decl) => {
|
||||||
|
if (decl.type === 'decl') {
|
||||||
|
style[decl.prop] = decl.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
styles[rule.selector] = style;
|
||||||
|
} else {
|
||||||
|
css.push(rule.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
styles,
|
||||||
|
css: css.join('\n')
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,7 +4,8 @@ import {
|
|||||||
type NodeEvents,
|
type NodeEvents,
|
||||||
type JSExpression,
|
type JSExpression,
|
||||||
type NodeDirective,
|
type NodeDirective,
|
||||||
type BlockSlot
|
type BlockSlot,
|
||||||
|
type JSFunction
|
||||||
} from '@vtj/core';
|
} from '@vtj/core';
|
||||||
import { compileTemplate } from '@vue/compiler-sfc';
|
import { compileTemplate } from '@vue/compiler-sfc';
|
||||||
import {
|
import {
|
||||||
@@ -20,13 +21,29 @@ import {
|
|||||||
import { uid } from '@vtj/base';
|
import { uid } from '@vtj/base';
|
||||||
import { isJSExpression, isNodeSchema } from '../shared';
|
import { isJSExpression, isNodeSchema } from '../shared';
|
||||||
import { getJSExpression, getJSFunction } from './utils';
|
import { getJSExpression, getJSFunction } from './utils';
|
||||||
|
import type { CSSRules } from './style';
|
||||||
|
|
||||||
let __slots: BlockSlot[] = [];
|
let __slots: BlockSlot[] = [];
|
||||||
let __context: Record<string, Set<string>> = {};
|
let __context: Record<string, Set<string>> = {};
|
||||||
|
let __handlers: Record<string, JSFunction> = {};
|
||||||
|
let __styles: CSSRules = {};
|
||||||
|
|
||||||
export function parseTemplate(id: string, name: string, content: string = '') {
|
export interface ParseTemplateOptions {
|
||||||
|
handlers?: Record<string, JSFunction>;
|
||||||
|
styles?: CSSRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseTemplate(
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
content: string = '',
|
||||||
|
options?: ParseTemplateOptions
|
||||||
|
) {
|
||||||
__slots = [];
|
__slots = [];
|
||||||
__context = {};
|
__context = {};
|
||||||
|
__handlers = options?.handlers || {};
|
||||||
|
__styles = options?.styles || {};
|
||||||
|
|
||||||
const result = compileTemplate({
|
const result = compileTemplate({
|
||||||
id,
|
id,
|
||||||
filename: name,
|
filename: name,
|
||||||
@@ -64,7 +81,21 @@ function getProps(nodes: Array<AttributeNode | DirectiveNode>) {
|
|||||||
for (const item of nodes) {
|
for (const item of nodes) {
|
||||||
// 普通属性
|
// 普通属性
|
||||||
if (item.type === NodeTypes.ATTRIBUTE) {
|
if (item.type === NodeTypes.ATTRIBUTE) {
|
||||||
props[item.name] = item.value?.content || '';
|
if (item.name === 'class') {
|
||||||
|
const classValue = item.value?.content || '';
|
||||||
|
const classRegex = /[\w]+_[\w]{5,}/;
|
||||||
|
const selector = classValue.match(classRegex)?.[0] || '';
|
||||||
|
const classes = classValue.split(' ').filter((n) => n !== selector);
|
||||||
|
const style = __styles[`.${selector}`];
|
||||||
|
if (style) {
|
||||||
|
props.style = style;
|
||||||
|
}
|
||||||
|
if (classes.length) {
|
||||||
|
props.class = classes.join(' ');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
props[item.name] = item.value?.content || '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 动态绑定的属性
|
// 动态绑定的属性
|
||||||
@@ -81,7 +112,10 @@ function getProps(nodes: Array<AttributeNode | DirectiveNode>) {
|
|||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEvents(nodes: Array<AttributeNode | DirectiveNode>) {
|
function getEvents(
|
||||||
|
nodes: Array<AttributeNode | DirectiveNode>,
|
||||||
|
handlers: Record<string, JSFunction> = {}
|
||||||
|
) {
|
||||||
const events: NodeEvents = {};
|
const events: NodeEvents = {};
|
||||||
for (const item of nodes) {
|
for (const item of nodes) {
|
||||||
// 动态绑定的属性
|
// 动态绑定的属性
|
||||||
@@ -97,11 +131,23 @@ function getEvents(nodes: Array<AttributeNode | DirectiveNode>) {
|
|||||||
},
|
},
|
||||||
{} as Record<string, boolean>
|
{} as Record<string, boolean>
|
||||||
);
|
);
|
||||||
events[item.arg.content] = {
|
const code = item.exp?.loc.source || '';
|
||||||
name: item.arg.content,
|
const regex = new RegExp(`${item.arg.content}_\[\\w\]\{5\,\}`);
|
||||||
handler: getJSFunction(`(${item.exp?.loc.source})`),
|
const name = code.match(regex)?.[0] || '';
|
||||||
modifiers
|
const handler = handlers[name];
|
||||||
};
|
if (name && handler) {
|
||||||
|
events[item.arg.content] = {
|
||||||
|
name: item.arg.content,
|
||||||
|
handler,
|
||||||
|
modifiers
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
events[item.arg.content] = {
|
||||||
|
name: item.arg.content,
|
||||||
|
handler: getJSFunction(`(${code})`),
|
||||||
|
modifiers
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +278,7 @@ function createNodeSchema(
|
|||||||
const dsl: NodeSchema = {
|
const dsl: NodeSchema = {
|
||||||
name: node.tag,
|
name: node.tag,
|
||||||
props: getProps(node.props),
|
props: getProps(node.props),
|
||||||
events: getEvents(node.props),
|
events: getEvents(node.props, __handlers),
|
||||||
directives: getDirectives(scope || node)
|
directives: getDirectives(scope || node)
|
||||||
};
|
};
|
||||||
dsl.id = getNodeId(dsl);
|
dsl.id = getNodeId(dsl);
|
||||||
|
|||||||
@@ -2,11 +2,7 @@ import { expect, test } from 'vitest';
|
|||||||
import { tsFormatter } from '@vtj/coder';
|
import { tsFormatter } from '@vtj/coder';
|
||||||
import { parseUniApp, parseVue } from '../src';
|
import { parseUniApp, parseVue } from '../src';
|
||||||
import { App } from './UniApp';
|
import { App } from './UniApp';
|
||||||
import { template1 } from './template';
|
import { template1, dependencies } from './template';
|
||||||
|
|
||||||
const dependencies = {
|
|
||||||
'element-plus': ['ElInput', 'ElButton']
|
|
||||||
};
|
|
||||||
|
|
||||||
// test('index', async () => {
|
// test('index', async () => {
|
||||||
// const result = parseUniApp(App);
|
// const result = parseUniApp(App);
|
||||||
@@ -19,7 +15,8 @@ test('template1', async () => {
|
|||||||
const result = await parseVue({
|
const result = await parseVue({
|
||||||
id: '235w0t1w',
|
id: '235w0t1w',
|
||||||
name: 'Bbb',
|
name: 'Bbb',
|
||||||
source: template1
|
source: template1,
|
||||||
|
dependencies: dependencies as any
|
||||||
});
|
});
|
||||||
console.log(JSON.stringify(result, null, 2));
|
console.log(JSON.stringify(result, null, 2));
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +1,197 @@
|
|||||||
|
export const dependencies = [
|
||||||
|
{
|
||||||
|
package: 'vue',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'Vue',
|
||||||
|
urls: ['@vtj/materials/deps/vue/vue.global.prod.js'],
|
||||||
|
assetsLibrary: 'VueMaterial',
|
||||||
|
required: true,
|
||||||
|
official: true,
|
||||||
|
enabled: true,
|
||||||
|
platform: ['web', 'h5']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: 'vue-router',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'VueRouter',
|
||||||
|
urls: ['@vtj/materials/deps/vue-router/vue-router.global.prod.js'],
|
||||||
|
assetsLibrary: 'VueRouterMaterial',
|
||||||
|
required: true,
|
||||||
|
official: true,
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: '@vtj/utils',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'VtjUtils',
|
||||||
|
urls: ['@vtj/materials/deps/@vtj/utils/index.umd.js'],
|
||||||
|
required: true,
|
||||||
|
official: true,
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: '@vtj/icons',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'VtjIcons',
|
||||||
|
urls: [
|
||||||
|
'@vtj/materials/deps/@vtj/icons/style.css',
|
||||||
|
'@vtj/materials/deps/@vtj/icons/index.umd.js'
|
||||||
|
],
|
||||||
|
required: true,
|
||||||
|
official: true,
|
||||||
|
enabled: true,
|
||||||
|
platform: ['web', 'h5']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: '@vueuse/core',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'VueUse',
|
||||||
|
urls: [
|
||||||
|
'@vtj/materials/deps/@vueuse/shared/index.iife.min.js',
|
||||||
|
'@vtj/materials/deps/@vueuse/core/index.iife.min.js'
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
official: true,
|
||||||
|
enabled: true,
|
||||||
|
platform: ['web', 'h5']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: 'element-plus',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'ElementPlus',
|
||||||
|
localeLibrary: 'ElementPlusLocaleZhCn',
|
||||||
|
urls: [
|
||||||
|
'@vtj/materials/deps/element-plus/dark/css-vars.css',
|
||||||
|
'@vtj/materials/deps/element-plus/index.css',
|
||||||
|
'@vtj/materials/deps/element-plus/zh-cn.js',
|
||||||
|
'@vtj/materials/deps/element-plus/index.full.min.js'
|
||||||
|
],
|
||||||
|
assetsUrl: '@vtj/materials/assets/element/index.umd.js',
|
||||||
|
assetsLibrary: 'ElementPlusMaterial',
|
||||||
|
required: false,
|
||||||
|
official: true,
|
||||||
|
enabled: true,
|
||||||
|
platform: 'web'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: '@vtj/ui',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'VtjUI',
|
||||||
|
urls: [
|
||||||
|
'@vtj/materials/deps/vxe-table/style.min.css',
|
||||||
|
'@vtj/materials/deps/@vtj/ui/style.css',
|
||||||
|
'@vtj/materials/deps/xe-utils/xe-utils.umd.min.js',
|
||||||
|
'@vtj/materials/deps/vxe-table/index.umd.min.js',
|
||||||
|
'@vtj/materials/deps/@vtj/ui/index.umd.js'
|
||||||
|
],
|
||||||
|
assetsUrl: '@vtj/materials/assets/ui/index.umd.js',
|
||||||
|
assetsLibrary: 'VtjUIMaterial',
|
||||||
|
required: false,
|
||||||
|
official: true,
|
||||||
|
enabled: true,
|
||||||
|
platform: 'web'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: 'ant-design-vue',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'antd',
|
||||||
|
urls: [
|
||||||
|
'@vtj/materials/deps/ant-design-vue/reset.css',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/dayjs/dayjs.min.js',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/dayjs/plugin/customParseFormat.js',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/dayjs/plugin/weekday.js',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/dayjs/plugin/localeData.js',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/dayjs/plugin/weekOfYear.js',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/dayjs/plugin/weekYear.js',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/dayjs/plugin/advancedFormat.js',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/dayjs/plugin/quarterOfYear.js',
|
||||||
|
'@vtj/materials/deps/ant-design-vue/antd.min.js'
|
||||||
|
],
|
||||||
|
assetsUrl: '@vtj/materials/assets/antdv/index.umd.js',
|
||||||
|
assetsLibrary: 'AntdvMaterial',
|
||||||
|
required: false,
|
||||||
|
official: true,
|
||||||
|
enabled: false,
|
||||||
|
platform: ['web']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: '@vtj/charts',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'VtjCharts',
|
||||||
|
urls: [
|
||||||
|
'@vtj/materials/deps/echarts/echarts.min.js',
|
||||||
|
'@vtj/materials/deps/@vtj/charts/index.umd.js'
|
||||||
|
],
|
||||||
|
assetsUrl: '@vtj/materials/assets/charts/index.umd.js',
|
||||||
|
assetsLibrary: 'VtjChartsMaterial',
|
||||||
|
required: false,
|
||||||
|
official: true,
|
||||||
|
enabled: true,
|
||||||
|
platform: ['web', 'h5']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: 'mockjs',
|
||||||
|
version: 'latest',
|
||||||
|
library: 'Mock',
|
||||||
|
urls: ['@vtj/materials/deps/mockjs/mock-min.js'],
|
||||||
|
required: false,
|
||||||
|
official: true,
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export const template1 = `
|
export const template1 = `
|
||||||
<template>
|
<template>
|
||||||
<XPanel
|
<XPanel
|
||||||
v-for="(item, index) in 3"
|
v-for="(item, index) in 3"
|
||||||
header="标题"
|
header="标题"
|
||||||
@click.stop="(...args: any[]) => click_13mntm28({ item, index }, args)">
|
@click.stop="(...args: any[]) => click_13mxuu2q({ item, index }, args)">
|
||||||
<div class="my-div div_193l8saav">
|
<div class="my-div div_193l8saav">
|
||||||
<span> {{ item }}</span>
|
<span> {{ item }}</span>
|
||||||
</div></XPanel
|
</div></XPanel
|
||||||
>
|
>
|
||||||
<ElButton type="primary" @click="click_13mph5o7"> 按钮</ElButton>
|
<ElButton type="primary" @click="click_33mxuu2q"> 按钮</ElButton>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { defineComponent, reactive } from 'vue';
|
import { defineComponent, reactive } from 'vue';
|
||||||
import { XPanel } from '@vtj/ui';
|
import { XPanel } from '@vtj/ui';
|
||||||
import { ElButton } from 'element-plus';
|
import { ElButton } from 'element-plus';
|
||||||
|
import { dateFormat } from '@vtj/utils';
|
||||||
import { useProvider } from '@vtj/renderer';
|
import { useProvider } from '@vtj/renderer';
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Bbb',
|
name: 'Bbb',
|
||||||
components: { XPanel, ElButton },
|
components: { XPanel, ElButton },
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const provider = useProvider({ id: '13dbje0g', version: '1743564663400' });
|
const provider = useProvider({ id: '13dbje0g', version: '1743578537999' });
|
||||||
const state = reactive({});
|
const state = reactive({});
|
||||||
return { state, props, provider };
|
return { state, props, provider, dateFormat };
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
click_13mntm28({ item, index }, args) {
|
click_13mxuu2q({ item, index }, args) {
|
||||||
return (() => {
|
return (() => {
|
||||||
console.log('click panel!', item);
|
console.log('click panel!', item);
|
||||||
}).apply(this, args);
|
}).apply(this, args);
|
||||||
},
|
},
|
||||||
click_13mph5o7(e) {
|
click_33mxuu2q(e) {
|
||||||
console.log('click button!', e);
|
console.log('click button!', e);
|
||||||
|
console.log(dateFormat(new Date(), 'YYYY-MM-DD'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.my-div {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.div_193l8saav {
|
||||||
|
padding-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -822,6 +822,9 @@ importers:
|
|||||||
'@vue/compiler-sfc':
|
'@vue/compiler-sfc':
|
||||||
specifier: ~3.5.13
|
specifier: ~3.5.13
|
||||||
version: 3.5.13
|
version: 3.5.13
|
||||||
|
postcss:
|
||||||
|
specifier: ~8.5.0
|
||||||
|
version: 8.5.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@vtj/cli':
|
'@vtj/cli':
|
||||||
specifier: workspace:~
|
specifier: workspace:~
|
||||||
|
|||||||
Reference in New Issue
Block a user