cocos Viewer

这是一个展示cocos节点树的工具

// ==UserScript==
// @name         cocos Viewer
// @namespace    http://tampermonkey.net/
// @version      2024-02-18
// @description  这是一个展示cocos节点树的工具
// @author       放学我走了
// @include      *://172.16.16.125/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=16.124
// @require      http://172.16.16.125/ccc-devtools/libs/js/vue.min.js
// @require      http://172.16.16.125/ccc-devtools/libs/js/vuetify4.js
// @resource customCSS1 http://172.16.16.125/ccc-devtools/libs/css/materialdesignicons.min4.css
// @resource customCSS2 http://172.16.16.125/ccc-devtools/libs/css/vuetify.min.css
// @resource customCSS3 http://172.16.16.125/ccc-devtools/libs/css/style4.css
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @license MIT
// ==/UserScript==
(function() {

    'use strict';
    // Your code here...

    const initConsoleUtil = function () {
        try {
            if (cc) {

            }else {
                return;
            }
        } catch (error) {
            return;
        }

        if (cc.tree) return;
        cc.tree = function (key) {
            let index = key || 0;
            let treeNode = function (node) {
                let nameStyle =
                    `color: ${node.parent === null || node.activeInHierarchy ? 'green' : 'grey'}; font-size: 14px;font-weight:bold`;
                let propStyle =
                    `color: black; background: lightgrey;margin-left: 5px;border-radius:3px;padding: 0 3px;font-size: 10px;font-weight:bold`;
                let indexStyle =
                    `color: orange; background: black;margin-left: 5px;border-radius:3px;padding:0 3px;fonrt-size: 10px;font-weight:bold;`
            let nameValue = `%c${node.name}`;
                let propValue =
                    `%c${node.x.toFixed(0) + ',' + node.y.toFixed(0) + ',' + node.width.toFixed(0) + ',' + node.height.toFixed(0) + ',' + node.scale.toFixed(1)}`
            let indexValue = `%c${index++}`;
                if (node.childrenCount > 0) {
                    console.groupCollapsed(nameValue + propValue + indexValue, nameStyle,
                                           propStyle, indexStyle);
                    for (let i = 0; i < node.childrenCount; i++) {
                        treeNode(node.children[i]);
                    }
                    console.groupEnd();
                } else {
                    console.log(nameValue + propValue + indexValue, nameStyle, propStyle,
                                indexStyle);
                }
            }
            if (key) {
                let node = cc.cat(key);
                index = node['tempIndex'];
                treeNode(node);
            } else {
                let scene = cc.director.getScene();
                treeNode(scene);
            }
            return '属性依次为x,y,width,height,scale.使用cc.cat(id)查看详细属性.';
        }
        cc.cat = function (key) {
            let index = 0;
            let target;
            let sortId = function (node) {
                if (target) return;
                if (cc.js.isNumber(key)) {
                    if (key === index++) {
                        target = node;
                        return;
                    }
                } else {
                    if (key.toLowerCase() === node.name.toLowerCase()) {
                        target = node;
                        return;
                    } else {
                        index++;
                    }
                }
                if (node.childrenCount > 0) {
                    for (let i = 0; i < node.childrenCount; i++) {
                        sortId(node.children[i]);
                    }
                }
            }
            let scene = cc.director.getScene();
            sortId(scene);
            target['tempIndex'] = cc.js.isNumber(key) ? key : index;
            return target;
        }
        cc.list = function (key) {
            let targets = [];
            let step = function (node) {
                if (node.name.toLowerCase().indexOf(key.toLowerCase()) > -1) {
                    targets.push(node);
                }
                if (node.childrenCount > 0) {
                    for (let i = 0; i < node.childrenCount; i++) {
                        step(node.children[i]);
                    }
                }
            }
            let scene = cc.director.getScene();
            step(scene);
            if (targets.length === 1) {
                return targets[0];
            } else {
                return targets;
            }
        }
        cc.where = function (key) {
            let target = key.name ? key : cc.cat(key);
            if (!target) {
                return null;
            }
            let rect = target.getBoundingBoxToWorld();
            let bgNode = new cc.Node();
            let graphics = bgNode.addComponent(cc.Graphics);
            let scene = cc.director.getScene();
            scene.addChild(bgNode);
            bgNode.position = rect.center;
            bgNode.group = target.group;
            bgNode.zIndex = cc.macro.MAX_ZINDEX;
            let isZeroSize = rect.width === 0 || rect.height === 0;
            if (isZeroSize) {
                graphics.circle(0, 0, 100);
                graphics.fillColor = cc.Color.GREEN;
                graphics.fill();
            } else {
                bgNode.width = rect.width;
                bgNode.height = rect.height;
                graphics.rect(-bgNode.width / 2, -bgNode.height / 2, bgNode.width, bgNode.height);
                graphics.fillColor = new cc.Color().fromHEX('#E91E6390');
                graphics.fill();
            }
            setTimeout(() => {
                if (cc.isValid(bgNode)) {
                    bgNode.destroy();
                }
            }, 2000);
            return target;
        }
        cc.cache = function () {
            if(cc.ENGINE_VERSION.indexOf("2.4")==-1){
             return cc.cache2();
            }
            let rawCacheData = cc.assetManager.assets._map;
            let cacheData = [];
            let totalTextureSize = 0;
            for (let k in rawCacheData) {
                let item = rawCacheData[k];
                if (item.type !== 'js' && item.type !== 'json') {
                    let itemName = '_';
                    let preview = '';
                    let content = item.__classname__;
                    let formatSize = -1;
                    if (item.type === 'png' || item.type === 'jpg') {
                        let texture = rawCacheData[k.replace('.' + item.type, '.json')];
                        if (texture && texture._owner && texture._owner._name) {
                            itemName = texture._owner._name;
                            preview = texture.content.url;
                        }
                    } else {
                        if (item.name) {
                            itemName = item.name;
                        } else if (item._owner) {
                            itemName = (item._owner && item._owner.name) || '_';
                        }
                        if (content === 'cc.Texture2D') {
                            preview = item.nativeUrl;
                            let textureSize = item.width * item.height * ((item._native === '.jpg' ? 3 : 4) / 1024 / 1024);
                            totalTextureSize += textureSize;
                            // sizeStr = textureSize.toFixed(3) + 'M';
                            formatSize = Math.round(textureSize * 1000) / 1000;
                        } else if (content === 'cc.SpriteFrame') {
                            preview = item._texture.nativeUrl;
                        }
                    }
                    cacheData.push({
                        queueId: item.queueId,
                        type: content,
                        name: itemName,
                        preview: preview,
                        id: item._uuid,
                        size: formatSize
                    });
                }
            }
            let cacheTitle = `缓存 [文件总数:${cacheData.length}][纹理缓存:${totalTextureSize.toFixed(2) + 'M'}]`;
            return [cacheData, cacheTitle];
        }
        cc.cache2=function(){
            let rawCacheData = cc.loader._cache;
            let cacheData = [];
            let totalTextureSize = 0;
            for (let k in rawCacheData) {
                let item = rawCacheData[k];
                let type=item.type;
                if (item.type !== 'js' && item.type !== 'json') {
                    let itemName = '_';
                    let preview = '';
                    let content = (item.content && item.content.__classname__) ? item.content.__classname__ : item.type;
                    let formatSize = -1;
                    if (item.type === 'png' || item.type === 'jpg') {
                        let texture = rawCacheData[k.replace('.' + item.type, '.json')];
                        if (texture && texture._owner && texture._owner._name) {
                            itemName = texture._owner._name;
                            preview = texture.content.url;
                        }
                    } else {
                        if (item.content.name && item.content.name.length > 0) {
                            itemName = item.content.name;
                        } else if (item._owner) {
                            itemName = (item._owner && item._owner.name) || '_';
                        }
                        if (content === 'cc.Texture2D') {
                            type=content;
                            let texture = item.content;
                            preview = texture.url;
                            let textureSize = texture.width * texture.height * ((texture._native === '.jpg' ? 3 : 4) / 1024 / 1024);
                            totalTextureSize += textureSize;
                            // sizeStr = textureSize.toFixed(3) + 'M';
                            formatSize = Math.round(textureSize * 1000) / 1000;
                        } else if (content === 'cc.SpriteFrame') {
                            preview = item.content._texture.url;
                        }
                    }
                    cacheData.push({
                        queueId: item.queueId,
                        type: type,
                        name: itemName,
                        preview: preview,
                        id: item.id,
                        content: content,
                        size: formatSize
                    });
                }
            }
            let cacheTitle = `缓存 [文件总数:${cacheData.length}][纹理缓存:${totalTextureSize.toFixed(2) + 'M'}]`;
            return [cacheData, cacheTitle];
        }
    }
    const NEX_CONFIG = {
        nodeSchema: {
            node2d: {
                title: 'Node',
                key: 'cc.Node',
                rows: [
                    { name: 'Name', key: 'name', type: 'text' },
                    { name: 'X', key: 'x', type: 'number' },
                    { name: 'Y', key: 'y', type: 'number' },
                    { name: 'Width', key: 'width', type: 'number' },
                    { name: 'Height', key: 'height', type: 'number' },
                    { name: 'Angle', key: 'angle', type: 'number' },
                    { name: 'ScaleX', key: 'scaleX', type: 'number' },
                    { name: 'ScaleY', key: 'scaleY', type: 'number' },
                    { name: 'Opacity', key: 'opacity', type: 'number' },
                    { name: 'Color', key: 'hex_color', type: 'color' },
                    { name: 'Group', key: 'group', type: 'text' },
                ]
            },
            node3d: {
                title: 'Node',
                key: 'cc.Node',
                rows: [
                    // TODO:
                ]
            },
        },
        componentsSchema: {
            'cc.Camera': {
                title: 'cc.Camera',
                key: 'cc.Camera',
                rows: [
                    { name: 'Zoom Ratio', key: 'zoomRatio', type: 'number' },
                    { name: 'Depth', key: 'depth', type: 'number' },
                    { name: 'Bacground Color', key: 'hex_backgroundColor', rawKey: 'backgroundColor', type: 'color' },
                    { name: 'Align with Screen', key: 'alignWithScreen', type: 'bool' },
                ]
            },
            'cc.Sprite': {
                key: 'cc.Sprite',
                title: 'cc.Sprite',
                rows: []
            },
            'cc.Label': {
                title: 'cc.Label',
                key: 'cc.Label',
                rows: [
                    { name: 'String', key: 'string', type: 'textarea' },
                    { name: 'Font Size', key: 'fontSize', type: 'number' },
                    { name: 'Line Height', key: 'lineHeight', type: 'number' },
                ]
            }
        }
    }


    function isCocos() {
        return new Promise((re, rj) => {
            let inter = setInterval(() => {
                try {
                    if (cc) {
                        window.clearInterval(inter);
                        re(cc)
                    }
                } catch (error) {

                }
            }, 100)
            });
    }

    function createApp(){
        // 创建一个div元素

        // 创建一个新的 div 元素
        var newDivElement = document.createElement('div');
        // 设置新 div 的一些属性和样式
        newDivElement.textContent = '这是新的 div';
        //newDivElement.style.zIndex="8888";
        newDivElement.style.width = '100%';
        newDivElement.style.height = '100%';
        //newDivElement.style.display = 'flex';
        //newDivElement.style.flexDirection = 'column';
        //newDivElement.style.justifyContent = 'center';
        // 获取父元素的引用
        var parentElement = document.getElementById('GameCanvas'); // 
        // 在父元素的前面插入新的 div 元素
        parentElement.parentNode.insertBefore(newDivElement, parentElement);
        newDivElement.innerHTML=getHtml();
        let inter = setInterval(() => {
            try {
                var elementToMove = document.getElementById('Cocos2dGameContainer');
                var targetElement = document.getElementById('GameDiv');
                // 移动元素到目标元素的内部
                if (elementToMove&&targetElement) {
                    window.clearInterval(inter);
                    targetElement.appendChild(elementToMove);
                    setInterval(()=>{shipei(); },1000)
                }
            } catch (error) {
            }
        }, 100)
        }
    function shipei(){
        cc.director.getScene().getChildByName("Canvas").scale=0.45;
        return;
        var elementToMove = document.getElementById('Cocos2dGameContainer');
        var myDiv = document.getElementById('GameDiv'); // 替换为实际的 <div> 的 id
        var rect = myDiv.getBoundingClientRect();
        var w=rect.width;
        var h=rect.height;
        let ws= w/1136;
        let hs= h/640;
        if(ws<hs){
            h=640*ws;
            //h=h*ws;
        }else {
            w=1136*hs;
            //w=w*hs;
        }
        myDiv.style.width = w+'px%';
        myDiv.style.height = h+'px%';
        console.log(`==============${w}===${h}====`);
        var resizeEvent = new UIEvent('resize', { bubbles: true, cancelable: true });
        window.dispatchEvent(resizeEvent);
    }


    async function appMain(){
        const css1 = GM_getResourceText("customCSS1");
        GM_addStyle(css1);
        const css2 = GM_getResourceText("customCSS2");
        GM_addStyle(css2);
        const css3 = GM_getResourceText("customCSS3");
        GM_addStyle(css3);
        await isCocos();
        console.log("=========createTreeButton=========")
        createApp();
        initVue();
        initConsoleUtil();

    }

    function initVue(){

        const app = new Vue({
            el: '#app',
            vuetify: new Vuetify({
                theme: { dark: true }
            }),
            data: {
                isShowTop: true,
                drawer: false,
                cacheDialog: false,
                cacheTitle: '',
                cacheHeaders: [
                    { text: 'Type', value: 'type' },
                    { text: 'Name', value: 'name' },
                    { text: 'Preivew', value: 'preview' },
                    { text: 'ID', value: 'id' },
                    { text: 'Size', value: 'size' },
                ],
                cacheRawData: [],
                cacheData: [],
                cacheSearchText: null,
                cacheOnlyTexture: true,
                treeData: [],
                selectedNodes: [],
                intervalId: -1,
                treeSearchText: null,
                nodeSchema: {},
                componentsSchema: [],
            },
            created() {

                if (this.isShowTop) {
                    this.startUpdateTree();
                }
                this.waitCCInit().then(() => {

                    initConsoleUtil();
                });
            },
            watch: {
                cacheOnlyTexture() {
                    this.updateCacheData();
                }
            },
            computed: {
                treeFilter() {
                    return (item, search, textKey) => item[textKey].indexOf(search) > -1;
                },
                selectedNode() {
                    if (!this.selectedNodes.length) return undefined
                    let node = getNodeById(this.selectedNodes[0]);
                    if (node) {
                        if (!node.hex_color) {
                            cc.js.getset(node, 'hex_color', () => {
                                return '#' + node.color.toHEX('#rrggbb');
                            }, (hex) => {
                                node.color = new cc.Color().fromHEX(hex);
                            }, false, true);
                        }

                        let superPreLoad = node._onPreDestroy;
                        node._onPreDestroy = () => {
                            superPreLoad.apply(node);
                            if (this.selectedNodes.length > 0 && this.selectedNodes[0] === node._id) {
                                this.selectedNodes.pop();
                            }
                        }
                        this.nodeSchema = NEX_CONFIG.nodeSchema.node2d;
                        let componentsSchema = [];
                        for (let component of node._components) {
                            let schema = NEX_CONFIG.componentsSchema[component.__classname__];
                            if (schema) {
                                node[schema.key] = node.getComponent(schema.key);
                                for (let i = 0; i < schema.rows.length; i++) {
                                    if (schema.rows[i].type === 'color') {
                                        if (!node[schema.key][schema.rows[i].key]) {
                                            cc.js.getset(node[schema.key], schema.rows[i].key, () => {
                                                return '#' + node.getComponent(schema.key)[schema.rows[i].rawKey].toHEX('#rrggbb');
                                            }, (hex) => {
                                                node.getComponent(schema.key)[schema.rows[i].rawKey] = new cc.Color().fromHEX(hex);
                                            }, false, true);
                                        }
                                    }
                                }
                            } else {
                                schema = {
                                    title: component.__classname__,
                                    key: component.__classname__
                                };
                                node[schema.key] = node.getComponent(schema.key);
                            }
                            componentsSchema.push(schema);
                        }
                        this.componentsSchema = componentsSchema;
                    }
                    return node;
                },
            },
            methods: {
                waitCCInit() {
                    return new Promise((resolve, reject) => {
                        resolve();
                    });
                },
                refreshTree: function () {
                    if (!this.$data.drawer || !cc.director.getScene() || !cc.director.getScene().children) return;
                    this.$data.treeData = getChildren(cc.director.getScene());
                },
                startUpdateTree: function () {
                    this.$data.intervalId = setInterval(() => {
                        this.refreshTree();
                    }, 200);
                },
                stopUpdateTree: function () {
                    clearInterval(this.$data.intervalId);
                },
                outputNodeHandler(id) {
                    let i = 1;
                    while (window['temp' + i] !== undefined) {
                        i++;
                    }
                    window['temp' + i] = this.selectedNode;
                    console.log('temp' + i);
                    console.log(window['temp' + i]);
                },
                outputComponentHandler(component) {
                    let i = 1;
                    while (window['temp' + i] !== undefined) {
                        i++;
                    }
                    window['temp' + i] = this.selectedNode.getComponent(component);
                    console.log('temp' + i);
                    console.log(window['temp' + i]);
                },
                drawNodeRect() {
                    cc.where(this.selectedNode);
                },
                updateCacheData() {
                    if (this.$data.cacheOnlyTexture) {
                        this.$data.cacheData = this.$data.cacheRawData.filter(item => item.type === 'cc.Texture2D');
                    } else {
                        this.$data.cacheData = this.$data.cacheRawData;
                    }
                },
                openCacheDialog() {
                    [this.$data.cacheRawData, this.$data.cacheTitle] = cc.cache();
                    this.updateCacheData();
                    this.$data.cacheDialog = true;
                },
                openGithub() {
                    window.open('https://github.com/potato47/ccc-devtools');
                },
                openCocosForum() {
                    window.open('https://forum.cocos.com/');
                },
                openCocosDocs() {
                    window.open('https://docs.cocos.com/');
                }
            }
        });
        return app;
    }

    function getChildren(node) {
        return node.children.map(child => {
            let children = (child.children && child.children.length > 0) ? getChildren(child) : [];
            return { id: child._id, name: child.name, active: child.activeInHierarchy, children };
        });
    }

    function getNodeById(id) {
        let target;
        const search = function (node) {
            if (node._id === id) {
                target = node;
                return;
            }
            if (node.childrenCount) {
                for (let i = 0; i < node.childrenCount; i++) {
                    if (!target) {
                        search(node.children[i]);
                    }
                }
            }
        }
        const scene = cc.director.getScene();
        search(scene);
        return target;
    }

    function getHtml(){
        let y=window.location.href.substring(window.location.protocol.length+2,35)
        y=y.substring(0,y.lastIndexOf("/"));
        let html=` <v-app id="app">
    <v-app-bar app clipped-left color="gray" dense v-if="isShowTop">
        <v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>
        <div id="recompiling"><span>Recompiling...</span></div>
        <v-spacer></v-spacer>
        <div class="toolbar">
            <div class="item">
                <v-btn id="btn-show-fps" small height="25"><span style="color: #aaa;">Show FPS</span></v-btn>
            </div>
           <div class="item">
                <v-btn id="btn-show-fps" small height="25" @click="openCacheDialog" ><span style="color: #aaa;">显示缓存资源</span></v-btn>
            </div>

        </div>
    </v-app-bar>
<v-navigation-drawer v-model="drawer" app clipped fixed width="512" v-if="isShowTop">
        <v-container style="height: 50%;overflow: auto;">
            <v-text-field v-model="treeSearchText" dense label="Search Node or Component" dark flat solo-inverted
                hide-details clearable clear-icon="mdi-close-circle-outline"></v-text-field>
            <v-treeview :items="treeData" item-key="id" dense activatable :search="treeSearchText"
                :active.sync="selectedNodes">
                <template v-slot:label="{ item, active }">
                    <label v-if="item.active" style="color: white;">{{ item.name }}</label>
                    <label v-else style="color: gray;">{{ item.name }}</label>
                </template>
            </v-treeview>
        </v-container>
        <v-container style="border-top: 2px solid darkgray;height: 50%;overflow-y: auto;">
            <template v-if="selectedNode">
                <!-- Node -->
                <table style="width: 100%;color: white;" border="1">
                    <thead>
                        <tr>
                            <th colspan="2" style="text-align: left; padding: 10px;">
                                <div class="float-left" style="display:inline-flex;">
                                    <v-simple-checkbox v-model="selectedNode.active"></v-simple-checkbox>
                                    <span style="margin-left: 10px;">{{ nodeSchema.title }}</span>
                                </div>
                                <div class="float-right">
                                    <v-icon style="margin-left: 10px;margin-right: 10px;" @click="drawNodeRect()">
                                        mdi-adjust</v-icon>
                                    <v-icon @click="outputNodeHandler()">mdi-send</v-icon>
                                </div>
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr v-for="row in nodeSchema.rows" :key="row.key">
                            <td style="padding: 10px;width: 40%;">{{ row.name }}</td>
                            <td style="width: 60%;">
                                <v-color-picker v-if="row.type == 'color'" class="ma-2" canvas-height="80" width="259"
                                    v-model="selectedNode[row.key]"></v-color-picker>
                                <v-simple-checkbox v-else-if="row.type == 'bool'" v-model="selectedNode[row.key]"
                                    style="padding: 10px;width: 100%;"></v-simple-checkbox>
                                <input v-else :type="row.type" v-model="selectedNode[row.key]"
                                    style="padding: 10px;width: 100%;"></input>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <!-- Components -->
                <table v-for="component in componentsSchema" style="width: 100%;color: white;" border="1">
                    <thead>
                        <tr>
                            <th colspan="2" style="text-align: left; padding: 10px;">
                                <div class="float-left" style="display:inline-flex;">
                                    <v-simple-checkbox v-model="selectedNode[component.key].enabled">
                                    </v-simple-checkbox>
                                    <span style="margin-left: 10px;">{{ component.title }}</span>
                                </div>
                                <div class="float-right">
                                    <v-icon @click="outputComponentHandler(component.key)">mdi-send</v-icon>
                                </div>
                            </th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr v-for="row in component.rows" :key="row.key">
                            <td style="padding: 10px;width: 40%;">{{ row.name }}</td>
                            <td style="width: 60%;">
                                <v-color-picker v-if="row.type == 'color'" class="ma-2" canvas-height="80" width="259"
                                    v-model="selectedNode[component.key][row.key]"></v-color-picker>
                                <textarea v-else-if="row.type == 'textarea'" rows="1"
                                    v-model="selectedNode[component.key][row.key]" style="padding: 10px;width: 100%;">
                                </textarea>
                                <v-simple-checkbox v-else-if="row.type == 'bool'"
                                    v-model="selectedNode[component.key][row.key]" style="padding: 10px;width: 100%;">
                                </v-simple-checkbox>
                                <input v-else :type="row.type" v-model="selectedNode[component.key][row.key]"
                                    style="padding: 10px;width: 100%;"></input>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </template>
        </v-container>
    </v-navigation-drawer>

        <v-content>
        <v-container fill-height>
            <div id="content" class="content">
                <div class="contentWrap">
                    <div id="GameDiv" class="wrapper">

                    </div>
                </div>
            </div>
        </v-container>
    </v-content>

    <v-dialog v-model="cacheDialog" persistent scrollable>
        <v-card>
            <v-card-title>
                {{ cacheTitle }}
                <v-spacer></v-spacer>
                <v-text-field v-model="cacheSearchText" append-icon="mdi-magnify" label="Search" single-line
                    hide-details>
                </v-text-field>
            </v-card-title>
            <v-divider></v-divider>
            <v-card-text>
                <v-data-table :headers="cacheHeaders" :items="cacheData" :search="cacheSearchText" :sort-by="['size']"
                    :sort-desc="[true]" :footer-props="{
                        showFirstLastPage: true,
                        firstIcon: 'mdi-chevron-double-left',
                        lastIcon: 'mdi-chevron-double-right',
                      }">
                    <template v-slot:item.size="{ item }">
                        {{ item.size == -1 ? '_' : (item.size +'MB') }}
                    </template>
                    <template v-slot:item.preview="{ item }">
                        <div style="height: 60px;display: flex;align-items: center;">
                            <img :src="window.location.protocol + '//${y}/' + item.preview"
                                style="max-height: 60px;max-width: 120px;" v-if="item.preview">
                            <template v-else>_</template>
                        </div>
                    </template>
                </v-data-table>
            </v-card-text>
            <v-divider></v-divider>
            <v-card-actions>
                <v-btn color="blue darken-1" text @click="cacheDialog = false">Close</v-btn>
                <v-spacer></v-spacer>
                <v-switch v-model="cacheOnlyTexture" label="只显示纹理"></v-switch>
            </v-card-actions>
        </v-card>
    </v-dialog>
    </v-app>
    `
        return html;

    }

    appMain();
})();