第一次提交
This commit is contained in:
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
518
src/App.vue
Normal file
518
src/App.vue
Normal file
@@ -0,0 +1,518 @@
|
||||
<template>
|
||||
<div class="navbar bg-base-100 shadow-sm">
|
||||
<div class="flex-none">
|
||||
<button class="btn btn-square btn-ghost">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="inline-block h-5 w-5 stroke-current">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
<input type="file" id="fileInput" class="file-input" @change="handleFileUpload" accept=".sbp"/>
|
||||
</div>
|
||||
|
||||
<div class="flex-none" v-if="parsedData">
|
||||
<button class="btn btn-ghost" @click="downloadCombinedData">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" />
|
||||
</svg>
|
||||
下载蓝图(.sbp)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mx-auto p-4">
|
||||
<div v-if="error" class="alert alert-error mb-4">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<span>{{ error }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="flex flex-col items-center justify-center py-8">
|
||||
<span class="loading loading-spinner loading-lg"></span>
|
||||
<p class="mt-4">正在解析蓝图文件...</p>
|
||||
</div>
|
||||
|
||||
<div v-if="parsedData" class="space-y-4">
|
||||
<!-- 文件头信息 -->
|
||||
<div class="collapse collapse-arrow bg-base-200 border border-base-300 rounded-box">
|
||||
<input type="checkbox" class="peer" checked />
|
||||
<div class="collapse-title font-medium">
|
||||
<span>文件头信息</span>
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-zebra">
|
||||
<tbody>
|
||||
<tr v-for="(value, key) in parsedData.header" :key="key">
|
||||
<th class="w-1/3">{{ key }}</th>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 材料资产 -->
|
||||
<div class="collapse collapse-arrow bg-base-200 border border-base-300 rounded-box">
|
||||
<input type="checkbox" class="peer" checked />
|
||||
<div class="collapse-title font-medium">
|
||||
<span>材料资产 ({{ parsedData.materials.length }})</span>
|
||||
</div>
|
||||
<div class="collapse-content space-y-4">
|
||||
<div v-for="(mat, index) in parsedData.materials" :key="index" class="bg-base-100 p-4 rounded-box">
|
||||
<div class="flex gap-2 items-end">
|
||||
<div class="form-control flex-1">
|
||||
<label class="label">
|
||||
<div class="tooltip tooltip-right" data-tip="此部分表达比较抽象,如果你不知道那个资源的路径是什么,可以找一个有这个资源的蓝图文件,解析后复制过来">
|
||||
<span class="label-text">路径 ⓘ</span>
|
||||
</div>
|
||||
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="mat.path"
|
||||
class="input input-bordered w-full"
|
||||
@input="updateMaterial(index, 'path', mat.path)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-control w-24">
|
||||
<label class="label">
|
||||
<span class="label-text">数量</span>
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
v-model.number="mat.amount"
|
||||
class="input input-bordered"
|
||||
@input="updateMaterial(index, 'amount', mat.amount)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-control w-24">
|
||||
<label class="label">
|
||||
<div class="tooltip tooltip-left tooltip-warning" data-tip="此数值只蓝图的最后一个才有,且绑定不得修改。如果删除此条则最后一个材料依然需要此数值">
|
||||
<span class="label-text">属性 ⓘ</span>
|
||||
</div>
|
||||
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="mat.attribute"
|
||||
class="input input-bordered"
|
||||
@input="updateMaterial(index, 'attribute', mat.attribute)"
|
||||
>
|
||||
</div>
|
||||
<button @click="removeMaterial(index)" class="btn btn-square btn-error">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click="addMaterial" class="btn btn-primary w-full">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||
</svg>
|
||||
添加材料
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 配方资产 -->
|
||||
<div class="collapse collapse-arrow bg-base-200 border border-base-300 rounded-box">
|
||||
<input type="checkbox" class="peer" />
|
||||
<div class="collapse-title font-medium">
|
||||
<span>配方资产 ({{ parsedData.recipes.length }})</span>
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table table-zebra">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>路径</th>
|
||||
<th>属性</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(recipe, index) in parsedData.recipes" :key="index">
|
||||
<td>{{ recipe.path }}</td>
|
||||
<td>{{ recipe.attribute }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 高级数据 -->
|
||||
<div class="collapse collapse-arrow bg-base-200 border border-base-300 rounded-box">
|
||||
<input type="checkbox" class="peer" />
|
||||
<div class="collapse-title font-medium">
|
||||
<span>高级数据</span>
|
||||
</div>
|
||||
<div class="collapse-content space-y-4">
|
||||
<div class="mockup-code">
|
||||
<pre data-prefix=">"><code>元数据:</code></pre>
|
||||
<pre data-prefix=">"><code>{{ parsedData.metadata_hex || '无元数据' }}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mockup-code">
|
||||
<pre data-prefix=">"><code>压缩数据:</code></pre>
|
||||
<pre data-prefix=">"><code>{{ parsedData.compressed_data_hex || '无压缩数据' }}</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="mockup-code">
|
||||
<pre data-prefix=">"><code>原始JSON数据:</code></pre>
|
||||
<pre><code>{{ parsedData }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 合并数据 -->
|
||||
<div class="collapse collapse-arrow bg-base-200 border border-base-300 rounded-box">
|
||||
<input type="checkbox" class="peer" />
|
||||
<div class="collapse-title font-medium">
|
||||
<span>合并数据 ({{ combinedData ? combinedData.length/2 : 0 }}字节)</span>
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div class="mockup-code">
|
||||
<pre data-prefix=">"><code>合并后的HEX数据:</code></pre>
|
||||
<pre><code>{{ combinedData || '无数据' }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selectedFile: null,
|
||||
parsedData: null,
|
||||
loading: false,
|
||||
error: null,
|
||||
combinedData: null
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
updateMaterial(index, field, value) {
|
||||
this.parsedData.materials[index][field] = value;
|
||||
this.updateMaterialRawData(index);
|
||||
this.combineData();
|
||||
},
|
||||
|
||||
updateMaterialRawData(index) {
|
||||
const mat = this.parsedData.materials[index];
|
||||
|
||||
// 更新raw_path
|
||||
mat.raw_path = this.stringToHex(mat.path) + "00";
|
||||
|
||||
// 更新raw_data_hex
|
||||
const pathHex = this.stringToHex(mat.path);
|
||||
const pathLengthHex = this.intToHexLE(pathHex.length / 2 + 1); // +1 for null terminator
|
||||
const amountHex = this.intToHexLE(mat.amount);
|
||||
const attributeHex = this.intToHexLE(mat.attribute);
|
||||
|
||||
mat.raw_data_hex = pathLengthHex.slice(0, 8) + pathHex + "00" + amountHex.slice(0, 8) + attributeHex.slice(0, 8);
|
||||
},
|
||||
|
||||
addMaterial() {
|
||||
const newMaterial = {
|
||||
path: "",
|
||||
amount: 0,
|
||||
attribute: 0,
|
||||
raw_path: "",
|
||||
raw_data_hex: ""
|
||||
};
|
||||
|
||||
this.parsedData.materials.push(newMaterial);
|
||||
this.updateMaterialRawData(this.parsedData.materials.length - 1);
|
||||
this.updateHeaderCount();
|
||||
this.combineData();
|
||||
},
|
||||
|
||||
removeMaterial(index) {
|
||||
this.parsedData.materials.splice(index, 1);
|
||||
this.updateHeaderCount();
|
||||
this.combineData();
|
||||
},
|
||||
|
||||
updateHeaderCount() {
|
||||
if (!this.parsedData?.header) return;
|
||||
|
||||
// 更新header中的material_count
|
||||
this.parsedData.header.material_count = this.parsedData.materials.length;
|
||||
|
||||
// 更新header_raw_hex中的material_count (位于第7个int32,偏移24字节)
|
||||
if (this.parsedData.header_raw_hex) {
|
||||
try {
|
||||
const headerBytes = this.hexToBytes(this.parsedData.header_raw_hex);
|
||||
const view = new DataView(headerBytes.buffer);
|
||||
view.setUint32(24, this.parsedData.materials.length, true);
|
||||
|
||||
this.parsedData.header_raw_hex = Array.from(new Uint8Array(headerBytes.buffer))
|
||||
.map(b => b.toString(16).padStart(2, '0'))
|
||||
.join('');
|
||||
} catch (e) {
|
||||
console.error('更新header_raw_hex失败:', e);
|
||||
this.error = '更新文件头数据失败';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
stringToHex(str) {
|
||||
let hex = "";
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hex += str.charCodeAt(i).toString(16).padStart(2, '0');
|
||||
}
|
||||
return hex;
|
||||
},
|
||||
|
||||
intToHexLE(num, bytes = 4) {
|
||||
let hex = "";
|
||||
for (let i = 0; i < bytes; i++) {
|
||||
hex += ((num >> (i * 8)) & 0xFF).toString(16).padStart(2, '0');
|
||||
}
|
||||
return hex;
|
||||
},
|
||||
|
||||
hexToBytes(hexString) {
|
||||
const cleanHex = hexString.replace(/\s/g, '');
|
||||
|
||||
if (!/^[0-9a-fA-F]*$/.test(cleanHex)) {
|
||||
throw new Error('无效的HEX字符串');
|
||||
}
|
||||
|
||||
if (cleanHex.length % 2 !== 0) {
|
||||
throw new Error('HEX字符串长度应为偶数');
|
||||
}
|
||||
|
||||
const bytes = new Uint8Array(cleanHex.length / 2);
|
||||
for (let i = 0; i < cleanHex.length; i += 2) {
|
||||
bytes[i/2] = parseInt(cleanHex.substr(i, 2), 16);
|
||||
}
|
||||
return bytes;
|
||||
},
|
||||
|
||||
combineData() {
|
||||
if (!this.parsedData) {
|
||||
this.combinedData = null;
|
||||
return '';
|
||||
}
|
||||
|
||||
let combined = '';
|
||||
|
||||
// 添加header_raw_hex
|
||||
if (this.parsedData.header_raw_hex) {
|
||||
combined += this.parsedData.header_raw_hex;
|
||||
}
|
||||
|
||||
// 添加所有材料的raw_data_hex
|
||||
if (this.parsedData.materials && this.parsedData.materials.length > 0) {
|
||||
this.parsedData.materials.forEach(mat => {
|
||||
if (mat.raw_data_hex) {
|
||||
combined += mat.raw_data_hex;
|
||||
}
|
||||
});
|
||||
}
|
||||
combined += "00000000"; // 材料数据结束标志
|
||||
|
||||
// 添加所有配方的raw_data_hex
|
||||
if (this.parsedData.recipes && this.parsedData.recipes.length > 0) {
|
||||
this.parsedData.recipes.forEach(recipe => {
|
||||
if (recipe.raw_data_hex) {
|
||||
combined += recipe.raw_data_hex;
|
||||
}
|
||||
});
|
||||
}
|
||||
combined += "22222222"; // 配方数据结束标志
|
||||
|
||||
// 添加压缩数据
|
||||
if (this.parsedData.compressed_data_hex) {
|
||||
combined += this.parsedData.compressed_data_hex;
|
||||
}
|
||||
|
||||
this.combinedData = combined;
|
||||
return combined;
|
||||
},
|
||||
|
||||
downloadCombinedData() {
|
||||
try {
|
||||
const data = this.combinedData || this.combineData();
|
||||
if (!data) {
|
||||
this.error = '没有可下载的数据';
|
||||
return;
|
||||
}
|
||||
|
||||
const bytes = this.hexToBytes(data);
|
||||
const blob = new Blob([bytes], { type: 'application/octet-stream' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'blueprint.sbp';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
this.error = '下载失败: ' + error.message;
|
||||
console.error('下载错误:', error);
|
||||
}
|
||||
},
|
||||
|
||||
handleFileUpload(event) {
|
||||
this.selectedFile = event.target.files[0];
|
||||
this.parsedData = null;
|
||||
this.combinedData = null;
|
||||
this.error = null;
|
||||
|
||||
if (this.selectedFile) {
|
||||
this.parseBlueprint();
|
||||
}
|
||||
},
|
||||
|
||||
async parseBlueprint() {
|
||||
if (!this.selectedFile) return;
|
||||
|
||||
this.loading = true;
|
||||
this.error = null;
|
||||
this.parsedData = null;
|
||||
this.combinedData = null;
|
||||
|
||||
try {
|
||||
const arrayBuffer = await this.readFileAsArrayBuffer(this.selectedFile);
|
||||
this.parsedData = this.parseBinaryData(arrayBuffer);
|
||||
this.combineData();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
this.error = '解析蓝图文件时出错: ' + error.message;
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
|
||||
readFileAsArrayBuffer(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = reject;
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
},
|
||||
|
||||
parseBinaryData(arrayBuffer) {
|
||||
const view = new DataView(arrayBuffer);
|
||||
let offset = 0;
|
||||
|
||||
// 1. 解析头部 (32字节)
|
||||
const header = {
|
||||
version: view.getUint32(offset, true),
|
||||
header_size: view.getUint32(offset + 4, true),
|
||||
timestamp: view.getUint32(offset + 8, true),
|
||||
unknown1: view.getUint32(offset + 12, true),
|
||||
unknown2: view.getUint32(offset + 16, true),
|
||||
unknown3: view.getUint32(offset + 20, true),
|
||||
material_count: view.getUint32(offset + 24, true),
|
||||
reserved: view.getUint32(offset + 28, true)
|
||||
};
|
||||
offset += 32;
|
||||
|
||||
// 保存原始头部数据
|
||||
const headerBytes = new Uint8Array(arrayBuffer.slice(0, 32));
|
||||
const header_raw_hex = Array.from(headerBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
|
||||
// 2. 解析材料部分
|
||||
const materials = [];
|
||||
for (let i = 0; i < header.material_count; i++) {
|
||||
const pathLength = view.getUint32(offset, true);
|
||||
offset += 4;
|
||||
|
||||
// 读取路径字符串
|
||||
const pathBytes = new Uint8Array(arrayBuffer.slice(offset, offset + pathLength - 1));
|
||||
const path = new TextDecoder().decode(pathBytes);
|
||||
offset += pathLength;
|
||||
|
||||
const amount = view.getUint32(offset, true);
|
||||
const attribute = view.getUint32(offset + 4, true);
|
||||
offset += 8;
|
||||
|
||||
// 生成原始数据HEX表示
|
||||
const pathHex = Array.from(pathBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
const pathLengthHex = this.intToHexLE(pathLength, 4);
|
||||
const amountHex = this.intToHexLE(amount, 4);
|
||||
const attributeHex = this.intToHexLE(attribute, 4);
|
||||
|
||||
materials.push({
|
||||
path,
|
||||
amount,
|
||||
attribute,
|
||||
raw_path: pathHex + "00",
|
||||
raw_data_hex: pathLengthHex + pathHex + "00" + amountHex + attributeHex
|
||||
});
|
||||
}
|
||||
|
||||
// 检查材料部分结束标记 (4字节0)
|
||||
const materialEnd = view.getUint32(offset, true);
|
||||
offset += 4;
|
||||
|
||||
// 3. 解析建筑部分
|
||||
const recipes = [];
|
||||
while (true) {
|
||||
// 检查是否到达建筑部分结束标记
|
||||
if (view.getUint32(offset, true) === 0x22222222) {
|
||||
offset += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
const pathLength = view.getUint32(offset, true);
|
||||
offset += 4;
|
||||
|
||||
// 读取路径字符串
|
||||
const pathBytes = new Uint8Array(arrayBuffer.slice(offset, offset + pathLength - 1));
|
||||
const path = new TextDecoder().decode(pathBytes);
|
||||
offset += pathLength;
|
||||
|
||||
const attribute = view.getUint32(offset, true);
|
||||
offset += 4;
|
||||
|
||||
// 生成原始数据HEX表示
|
||||
const pathHex = Array.from(pathBytes).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
const pathLengthHex = this.intToHexLE(pathLength, 4);
|
||||
const attributeHex = this.intToHexLE(attribute, 4);
|
||||
|
||||
recipes.push({
|
||||
path,
|
||||
attribute,
|
||||
raw_data_hex: pathLengthHex + pathHex + "00" + attributeHex
|
||||
});
|
||||
}
|
||||
|
||||
// 4. 读取压缩数据
|
||||
const compressedData = new Uint8Array(arrayBuffer.slice(offset));
|
||||
const compressed_data_hex = Array.from(compressedData).map(b => b.toString(16).padStart(2, '0')).join('');
|
||||
|
||||
return {
|
||||
header,
|
||||
header_raw_hex,
|
||||
materials,
|
||||
recipes,
|
||||
compressed_data_hex,
|
||||
compressed_data_size: compressedData.length
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 使用 DaisyUI 的类,无需额外样式 */
|
||||
</style>
|
||||
1
src/assets/vue.svg
Normal file
1
src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 496 B |
43
src/components/HelloWorld.vue
Normal file
43
src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineProps({
|
||||
msg: String,
|
||||
})
|
||||
|
||||
const count = ref(0)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<h1>{{ msg }}</h1>
|
||||
|
||||
<div class="card">
|
||||
<button type="button" @click="count++">count is {{ count }}</button>
|
||||
<p>
|
||||
Edit
|
||||
<code>components/HelloWorld.vue</code> to test HMR
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
Check out
|
||||
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||
>create-vue</a
|
||||
>, the official Vue + Vite starter
|
||||
</p>
|
||||
<p>
|
||||
Learn more about IDE Support for Vue in the
|
||||
<a
|
||||
href="https://vuejs.org/guide/scaling-up/tooling.html#ide-support"
|
||||
target="_blank"
|
||||
>Vue Docs Scaling up Guide</a
|
||||
>.
|
||||
</p>
|
||||
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
5
src/main.js
Normal file
5
src/main.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import './style.css'
|
||||
import App from './App.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
2
src/style.css
Normal file
2
src/style.css
Normal file
@@ -0,0 +1,2 @@
|
||||
@import "tailwindcss";
|
||||
@plugin "daisyui";
|
||||
7
vite.config.js
Normal file
7
vite.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue(),tailwindcss()],
|
||||
})
|
||||
Reference in New Issue
Block a user