mirror of
https://github.com/Dichgrem/Vue.git
synced 2025-12-16 21:51:59 -05:00
style:test
This commit is contained in:
6
test/test1.html
Normal file
6
test/test1.html
Normal file
@@ -0,0 +1,6 @@
|
||||
<!DOCTYPE <html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset=
|
||||
</head>
|
||||
</html>>
|
||||
31
test/vue1.html
Normal file
31
test/vue1.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" connect="width=devices-width,initial-scale=1.0" />
|
||||
<title>Vue3 demo</title>
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div style="text-align: center" id="app">
|
||||
<h1>{{ count }}</h1>
|
||||
<button v-on:click="clickButton">单击</button>
|
||||
</div>
|
||||
</body>
|
||||
<!--hello-->
|
||||
<script>
|
||||
const App = {
|
||||
data() {
|
||||
return {
|
||||
count: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
clickButton() {
|
||||
this.count = this.count + 1;
|
||||
},
|
||||
},
|
||||
};
|
||||
Vue.createApp(App).mount("#app");
|
||||
</script>
|
||||
</html>
|
||||
121
test/vue2.html
Normal file
121
test/vue2.html
Normal file
@@ -0,0 +1,121 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Vue 登录作业</title>
|
||||
<script src="https://unpkg.com/vue@3"></script>
|
||||
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 40px;
|
||||
}
|
||||
.container {
|
||||
width: 300px;
|
||||
margin: auto;
|
||||
}
|
||||
input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
button {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
margin: 8px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" class="container">
|
||||
<h2>
|
||||
{{ isLoggedIn ? ("欢迎回来!已登录账号:" + currentUser) : "请登录" }}
|
||||
</h2>
|
||||
|
||||
<!-- 登录表单 -->
|
||||
<div v-if="!isLoggedIn">
|
||||
<input type="text" v-model="studentId" placeholder="请输入账号(学号)">
|
||||
<input type="password" v-model="password" placeholder="请输入密码">
|
||||
<button @click="login">登录</button>
|
||||
</div>
|
||||
|
||||
<!-- 登出按钮 -->
|
||||
<div v-else>
|
||||
<button @click="logout">登出</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp } = Vue;
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
isLoggedIn: false,
|
||||
studentId: "",
|
||||
password: "",
|
||||
currentUser: "" // 保存当前登录用户
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async login() {
|
||||
if (!this.studentId || !this.password) {
|
||||
alert("请输入账号和密码!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 本地验证:支持两个测试账户
|
||||
if (
|
||||
(this.studentId === "2023001" && this.password === "123456") ||
|
||||
(this.studentId === "admin" && this.password === "admin")
|
||||
) {
|
||||
this.isLoggedIn = true;
|
||||
this.currentUser = this.studentId;
|
||||
alert("本地验证成功,已登录!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 否则再尝试服务器验证
|
||||
try {
|
||||
// 用 URLSearchParams 发送表单数据
|
||||
const params = new URLSearchParams();
|
||||
params.append("studentId", this.studentId);
|
||||
params.append("password", this.password);
|
||||
|
||||
const response = await axios.post("http://117.72.102.219/login.php", params, {
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded"
|
||||
}
|
||||
});
|
||||
|
||||
console.log("服务器响应:", response.data);
|
||||
|
||||
if (response.data === "success") {
|
||||
this.isLoggedIn = true;
|
||||
this.currentUser = this.studentId;
|
||||
alert("服务器验证成功!");
|
||||
} else {
|
||||
alert("账号或密码错误!");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
alert("登录失败,请检查网络或服务器!");
|
||||
}
|
||||
},
|
||||
logout() {
|
||||
this.isLoggedIn = false;
|
||||
this.studentId = "";
|
||||
this.password = "";
|
||||
this.currentUser = "";
|
||||
alert("您已退出登录!");
|
||||
}
|
||||
}
|
||||
}).mount("#app");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
134
test/vue3.html
Normal file
134
test/vue3.html
Normal file
@@ -0,0 +1,134 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-
|
||||
scale=1.0"
|
||||
/>
|
||||
<title>猜数游戏</title>
|
||||
|
||||
<script>
|
||||
let count = 10; // 猜测次数
|
||||
let target; // 随机目标数字
|
||||
let flag = false; // 用于控制游戏是否进行
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
text-align: center;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f0f8ff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
h2 {
|
||||
color: #333;
|
||||
}
|
||||
form {
|
||||
display: inline-block;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
input[type="text"] {
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
width: 100px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
input[type="button"] {
|
||||
padding: 5px 10px;
|
||||
margin: 5px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #007bff;
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
input[type="button"]:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin: 10px 0;思考如何使用 Vue3 实现该游戏?
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h2>猜数游戏</h2>
|
||||
<strong>请输入一个 0-100 之间的随机整数:</strong>
|
||||
<br /><br />
|
||||
<form>
|
||||
<input type="text" id="guess" placeholder="输入你的猜测" />
|
||||
<input type="button" id="button" value="提交" onclick="run()" />
|
||||
<input type="button" value="开始" onclick="restart()" />
|
||||
<br />
|
||||
<label
|
||||
>结果: <input type="text" id="feedback" readonly
|
||||
/></label>
|
||||
<label
|
||||
>当前还可以猜测的次数:
|
||||
<input type="text" value="10" id="count" readonly
|
||||
/></label>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
function restart() {
|
||||
flag = true; // 标记游戏开始
|
||||
let guess = document.getElementById("guess");
|
||||
guess.value = ""; // 清空输入框
|
||||
let result = document.getElementById("feedback");
|
||||
result.value = ""; // 清空反馈框
|
||||
count = 10; // 重置猜测次数
|
||||
let nn = document.getElementById("count");
|
||||
nn.value = count;
|
||||
target = Math.floor(Math.random() * 101); // 生成随机数
|
||||
}
|
||||
</script>
|
||||
|
||||
<script>
|
||||
function run() {
|
||||
if (flag) {
|
||||
let guess = parseInt(document.getElementById("guess").value);
|
||||
// 将用户输入转换为整数
|
||||
let result = document.getElementById("feedback");
|
||||
let nn = document.getElementById("count");
|
||||
// 判断用户输入是否有效
|
||||
if (isNaN(guess) || guess < 0 || guess > 100) {
|
||||
result.value = "请输入一个 0-100 之间的有效数字!";
|
||||
return;
|
||||
}
|
||||
// 根据猜测结果给出反馈
|
||||
if (guess > target) {
|
||||
result.value = "猜的有点大!";
|
||||
} else if (guess < target) {
|
||||
result.value = "猜的有点小!";
|
||||
} else if (guess == target) {
|
||||
result.value = "猜对啦!答案是" + guess;
|
||||
flag = false; // 猜对后结束游戏
|
||||
}
|
||||
// 更新剩余猜测次数
|
||||
count -= 1;
|
||||
nn.value = count;
|
||||
// 如果猜测次数用完,结束游戏
|
||||
if (count == 0 && guess != target) {
|
||||
result.value = "游戏结束!次数已用完,正确答案是" + target;
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
122
test/vue4.html
Normal file
122
test/vue4.html
Normal file
@@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>学生成绩计算</title>
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background: #f3f4f6;
|
||||
font-family: "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 6px 16px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #4f46e5;
|
||||
color: white;
|
||||
padding: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
td {
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100px;
|
||||
padding: 6px 8px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
border-color: #4f46e5;
|
||||
box-shadow: 0 0 4px rgba(79,70,229,0.4);
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
background: #f9fafb;
|
||||
color: #374151;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" class="card">
|
||||
<table>
|
||||
<tr>
|
||||
<th>学科</th>
|
||||
<th>分数</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>语文</td>
|
||||
<td><input v-model="chinese" type="text"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>数学</td>
|
||||
<td><input v-model="math" type="text"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>英语</td>
|
||||
<td><input v-model="english" type="text"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>总分</td>
|
||||
<td><input :value="total" readonly></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>平均分</td>
|
||||
<td><input :value="average" readonly></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp } = Vue;
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
chinese: 0,
|
||||
math: 0,
|
||||
english: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
total() {
|
||||
let c = Number(this.chinese) || 0;
|
||||
let m = Number(this.math) || 0;
|
||||
let e = Number(this.english) || 0;
|
||||
return c + m + e;
|
||||
},
|
||||
average() {
|
||||
return Math.floor(this.total / 3);
|
||||
}
|
||||
}
|
||||
}).mount("#app");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
105
test/vue5.html
Normal file
105
test/vue5.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Vue3 小型购物车</title>
|
||||
<script src="https://unpkg.com/vue@3"></script>
|
||||
<style>
|
||||
table thead tr {
|
||||
background-color: #ffebcd;
|
||||
}
|
||||
|
||||
table {
|
||||
margin: 0 auto;
|
||||
border: 1px solid #989898;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #989898;
|
||||
text-align: center;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin: 0 2px;
|
||||
padding: 2px 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<h2>购物车表格</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>书籍名称</th>
|
||||
<th>出版日期</th>
|
||||
<th>价格</th>
|
||||
<th>购买数量</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(book, index) in books" :key="book.id">
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td>{{ book.name }}</td>
|
||||
<td>{{ book.date }}</td>
|
||||
<td>¥{{ book.price.toFixed(2) }}</td>
|
||||
<td>
|
||||
<button @click="increase(book)">+</button>
|
||||
{{ book.count }}
|
||||
<button @click="decrease(book)">-</button>
|
||||
</td>
|
||||
<td>
|
||||
<button @click="remove(index)">移除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h3 style="text-align:center;">总价:¥{{ totalPrice.toFixed(2) }}</h3>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp } = Vue;
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
books: [
|
||||
{ id: 1, name: '《JavaScript程序设计》', date: '2022-9', price: 85.0, count: 1 },
|
||||
{ id: 2, name: '《C语言基础》', date: '2021-2', price: 59.0, count: 1 },
|
||||
{ id: 3, name: '《Java高级语言编程》', date: '2022-10', price: 39.0, count: 1 },
|
||||
{ id: 4, name: '《数据库原理》', date: '2023-3', price: 128.0, count: 1 },
|
||||
{ id: 5, name: '《计算机网络》', date: '2022-8', price: 88.0, count: 1 }
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
totalPrice() {
|
||||
return this.books.reduce((sum, book) => sum + book.price * book.count, 0);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
increase(book) {
|
||||
book.count++;
|
||||
},
|
||||
decrease(book) {
|
||||
if (book.count > 1) book.count--;
|
||||
},
|
||||
remove(index) {
|
||||
this.books.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}).mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
184
test/vue6.html
Normal file
184
test/vue6.html
Normal file
@@ -0,0 +1,184 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>任务六 简易计算器</title>
|
||||
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Microsoft YaHei", sans-serif;
|
||||
background: linear-gradient(135deg, #e3f2fd, #bbdefb);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
||||
padding: 30px 50px;
|
||||
text-align: center;
|
||||
width: 380px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
color: #1565c0;
|
||||
}
|
||||
|
||||
input, select, button {
|
||||
padding: 8px;
|
||||
margin: 8px 0;
|
||||
width: 80%;
|
||||
font-size: 16px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #ccc;
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
input:focus, select:focus {
|
||||
outline: none;
|
||||
border-color: #42a5f5;
|
||||
box-shadow: 0 0 5px rgba(66,165,245,0.4);
|
||||
}
|
||||
|
||||
button {
|
||||
background: #42a5f5;
|
||||
color: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
background: #1e88e5;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.result {
|
||||
margin-top: 15px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #2e7d32;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #d32f2f;
|
||||
margin-top: 10px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
ul {
|
||||
text-align: left;
|
||||
padding-left: 40px;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: disc;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.history {
|
||||
margin-top: 20px;
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="container">
|
||||
<h2>任务六 简易计算器</h2>
|
||||
|
||||
<p>请输入第一个数:</p>
|
||||
<input type="text" v-model.trim="num1" placeholder="支持小数与负数">
|
||||
|
||||
<p>
|
||||
<select v-model="op">
|
||||
<option value="+">+</option>
|
||||
<option value="-">-</option>
|
||||
<option value="*">×</option>
|
||||
<option value="/">÷</option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<p>请输入第二个数:</p>
|
||||
<input type="text" v-model.trim="num2" placeholder="支持小数与负数">
|
||||
|
||||
<p><button :disabled="!num1 || !num2" @click="calculate">计算</button></p>
|
||||
|
||||
<div class="result" v-if="result !== null">
|
||||
得出结果:{{ result }}
|
||||
</div>
|
||||
|
||||
<p class="error">{{ errorMsg }}</p>
|
||||
|
||||
<div class="history" v-if="history.length > 0">
|
||||
<h3>历史记录(最近5次)</h3>
|
||||
<ul>
|
||||
<li v-for="(item, index) in history" :key="index">{{ item }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp } = Vue;
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
num1: '',
|
||||
num2: '',
|
||||
op: '+',
|
||||
result: null,
|
||||
errorMsg: '',
|
||||
history: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
isValidNumber(val) {
|
||||
return /^-?\d+(\.\d+)?$/.test(val);
|
||||
},
|
||||
calculate() {
|
||||
this.errorMsg = '';
|
||||
this.result = null;
|
||||
|
||||
if (!this.isValidNumber(this.num1) || !this.isValidNumber(this.num2)) {
|
||||
this.errorMsg = '请输入合法的数字(支持小数与负数)。';
|
||||
return;
|
||||
}
|
||||
|
||||
let a = parseFloat(this.num1);
|
||||
let b = parseFloat(this.num2);
|
||||
|
||||
if (this.op === '/' && b === 0) {
|
||||
this.errorMsg = '除数不能为 0。';
|
||||
return;
|
||||
}
|
||||
|
||||
let res;
|
||||
switch (this.op) {
|
||||
case '+': res = a + b; break;
|
||||
case '-': res = a - b; break;
|
||||
case '*': res = a * b; break;
|
||||
case '/': res = a / b; break;
|
||||
}
|
||||
|
||||
this.result = Math.round(res * 100) / 100; // 保留两位小数
|
||||
|
||||
// 添加历史记录
|
||||
this.history.unshift(this.result);
|
||||
if (this.history.length > 5) this.history.pop();
|
||||
}
|
||||
}
|
||||
}).mount('#app');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
586
test/vue7.html
Normal file
586
test/vue7.html
Normal file
@@ -0,0 +1,586 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>4-4 购物车 · 添加图书</title>
|
||||
<script src="https://unpkg.com/vue@3.3.4/dist/vue.global.js"></script>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f6f8fa;
|
||||
--card: #ffffff;
|
||||
--muted: #6b7280;
|
||||
--text: #111827;
|
||||
--primary: #2563eb;
|
||||
--primary-600: #1e40af;
|
||||
--border: #e5e7eb;
|
||||
--chip: #eef2ff;
|
||||
--chip-text: #3730a3;
|
||||
--danger: #dc2626;
|
||||
--success: #059669;
|
||||
--shadow: 0 10px 24px rgba(0, 0, 0, .06);
|
||||
--radius: 14px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "PingFang SC", "Hiragino Sans GB";
|
||||
color: var(--text);
|
||||
background: radial-gradient(1200px 600px at 10% -10%, #eff6ff 0%, transparent 60%),
|
||||
radial-gradient(1000px 500px at 110% 10%, #fef3c7 0%, transparent 60%),
|
||||
var(--bg);
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1100px;
|
||||
margin: 32px auto 48px;
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.titlebar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 28px;
|
||||
letter-spacing: .4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
background: var(--primary);
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
/* One-column vertical stack */
|
||||
.stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
margin: 0 0 12px;
|
||||
font-size: 18px;
|
||||
letter-spacing: .3px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.subtle {
|
||||
font-size: 12px;
|
||||
color: #4b5563;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
.form-grid {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
grid-template-columns: 120px 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.form-grid label {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.form-grid label {
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.form-grid input[type="text"],
|
||||
.form-grid input[type="number"],
|
||||
.form-grid input[type="month"],
|
||||
.form-grid select {
|
||||
width: 100%;
|
||||
appearance: none;
|
||||
border: 1px solid var(--border);
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
outline: none;
|
||||
background: #fff;
|
||||
transition: box-shadow .2s, border-color .2s;
|
||||
}
|
||||
|
||||
.form-grid input:focus,
|
||||
.form-grid select:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 4px rgba(37, 99, 235, .12);
|
||||
}
|
||||
|
||||
.inline {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.radio,
|
||||
.checkbox {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.btn {
|
||||
appearance: none;
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
padding: 10px 14px;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
transition: transform .06s ease, box-shadow .2s ease, background .2s ease;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
box-shadow: 0 6px 18px rgba(37, 99, 235, .25);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: var(--primary-600);
|
||||
}
|
||||
|
||||
.btn-ghost {
|
||||
background: #fff;
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--danger);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* List (vertical cards) */
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
background: #fff;
|
||||
padding: 14px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.row:hover {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, .10), var(--shadow);
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 800;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.muted {
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.chips {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.chip {
|
||||
background: var(--chip);
|
||||
color: var(--chip-text);
|
||||
border: 1px solid #e0e7ff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.qty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.iconbtn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.iconbtn:hover {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, .12);
|
||||
}
|
||||
|
||||
.price {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.tag-on {
|
||||
color: var(--success);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: #fff;
|
||||
background: var(--danger);
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.danger:hover {
|
||||
filter: brightness(.95);
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 14px;
|
||||
padding: 12px 16px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.total {
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.empty {
|
||||
text-align: center;
|
||||
color: var(--muted);
|
||||
padding: 48px 16px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app" class="container">
|
||||
<div class="titlebar">
|
||||
<h1>图书购物车 </h1>
|
||||
</div>
|
||||
<div class="stack">
|
||||
<!-- 顶部:添加图书 -->
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h3>添加图书</h3>
|
||||
<div class="form-grid" style="margin-top:12px;">
|
||||
<label for="name">书名*</label>
|
||||
<input id="name" v-model="form.name" placeholder="例如:《现代操作系统》" type="text" />
|
||||
|
||||
<label for="author">作者*</label>
|
||||
<input id="author" v-model="form.author" placeholder="例如:Andrew S. Tanenbaum" type="text" />
|
||||
|
||||
<label for="date">出版日期*</label>
|
||||
<input id="date" v-model="form.date" type="month" />
|
||||
|
||||
<label for="price">价格*</label>
|
||||
<input id="price" v-model.number="form.price" type="number" step="0.01" min="0" placeholder="例如:88.00" />
|
||||
|
||||
<label for="num">数量*</label>
|
||||
<input id="num" v-model.number="form.num" type="number" min="1" />
|
||||
|
||||
<label for="category">类别*</label>
|
||||
<select id="category" v-model="form.category">
|
||||
<option value="">请选择</option>
|
||||
<option value="计算机基础">计算机基础</option>
|
||||
<option value="编程语言">编程语言</option>
|
||||
<option value="数据库">数据库</option>
|
||||
<option value="计算机网络">计算机网络</option>
|
||||
<option value="操作系统">操作系统</option>
|
||||
<option value="软件工程">软件工程</option>
|
||||
</select>
|
||||
|
||||
<label>格式*</label>
|
||||
<div class="inline">
|
||||
<label class="radio"><input type="radio" name="format" v-model="form.format" value="纸质书" /> 纸质书</label>
|
||||
<label class="radio"><input type="radio" name="format" v-model="form.format" value="电子书" /> 电子书</label>
|
||||
<label class="radio"><input type="radio" name="format" v-model="form.format" value="套装" /> 套装</label>
|
||||
</div>
|
||||
|
||||
<label>标签</label>
|
||||
<div class="inline">
|
||||
<label class="checkbox"><input type="checkbox" v-model="form.tags" value="教材" /> 教材</label>
|
||||
<label class="checkbox"><input type="checkbox" v-model="form.tags" value="经典" /> 经典</label>
|
||||
<label class="checkbox"><input type="checkbox" v-model="form.tags" value="实例丰富" /> 实例丰富</label>
|
||||
<label class="checkbox"><input type="checkbox" v-model="form.tags" value="考试推荐" /> 考试推荐</label>
|
||||
</div>
|
||||
|
||||
<label>是否现货</label>
|
||||
<div class="inline">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="form.inStock" :true-value="true" :false-value="false" />
|
||||
有现货
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<label for="language">语言</label>
|
||||
<select id="language" v-model="form.language">
|
||||
<option value="中文">中文</option>
|
||||
<option value="英文">英文</option>
|
||||
<option value="其他">其他</option>
|
||||
</select>
|
||||
|
||||
<label for="isbn">ISBN</label>
|
||||
<input id="isbn" v-model="form.isbn" placeholder="例如:978-7-xxxx-xxxxx-x" type="text" />
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button class="btn btn-primary" @click="addBook">添加到列表</button>
|
||||
<button class="btn btn-ghost" @click="resetForm">重置</button>
|
||||
<span class="error" v-if="errorMsg">{{ errorMsg }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部:垂直卡片列表 -->
|
||||
<div class="card" v-if="books.length">
|
||||
<div class="card-body">
|
||||
<h3>图书列表</h3>
|
||||
<div class="list" style="margin-top:10px;">
|
||||
<div class="row" v-for="item in books" :key="item.id">
|
||||
<div class="meta">
|
||||
<div class="title">{{ item.name }}</div>
|
||||
<div class="muted">作者:{{ item.author }} | 类别:{{ item.category }} | 格式:{{ item.format }}</div>
|
||||
<div class="muted">出版:{{ item.date }} | ISBN:{{ item.isbn }} | 价格:¥{{ item.price.toFixed(2)
|
||||
}} | 现货:<span :class="{'tag-on': item.inStock}">{{ item.inStock ? '是' : '否' }}</span></div>
|
||||
<div class="chips">
|
||||
<span class="chip" v-for="t in item.tags" :key="t">{{ t }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="qty">
|
||||
<button class="iconbtn" title="增加" @click="inc(item)">+</button>
|
||||
<strong>{{ item.num }}</strong>
|
||||
<button class="iconbtn" title="减少" @click="dec(item)">-</button>
|
||||
</div>
|
||||
<button class="btn danger" @click="remove(item.id)">移除</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<div class="muted">共 {{ books.length }} 本书</div>
|
||||
<div class="total">总价格:¥{{ sum.toFixed(2) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="empty card" v-else>
|
||||
<div class="card-body">
|
||||
<h3 style="margin:0 0 4px;">列表为空</h3>
|
||||
<div class="muted">请先在上方表单添加图书</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const { createApp } = Vue;
|
||||
|
||||
createApp({
|
||||
data() {
|
||||
return {
|
||||
books: [
|
||||
{ id: 1, name: "《JavaScript程序设计》", author: "张三", category: "编程语言", format: "纸质书", tags: ["教材", "经典"], inStock: true, language: "中文", isbn: "978-7-00000-000-1", date: "2022-09", price: 85.00, num: 1 },
|
||||
{ id: 2, name: "《C语言基础》", author: "李四", category: "编程语言", format: "纸质书", tags: ["教材"], inStock: true, language: "中文", isbn: "978-7-00000-000-2", date: "2021-02", price: 59.00, num: 1 },
|
||||
{ id: 3, name: "《Java高级语言编程》", author: "王五", category: "编程语言", format: "电子书", tags: ["经典", "实例丰富"], inStock: true, language: "中文", isbn: "978-7-00000-000-3", date: "2022-10", price: 39.00, num: 1 },
|
||||
{ id: 4, name: "《数据库原理》", author: "赵六", category: "数据库", format: "纸质书", tags: ["教材", "考试推荐"], inStock: false, language: "中文", isbn: "978-7-00000-000-4", date: "2023-03", price: 128.00, num: 1 },
|
||||
{ id: 5, name: "《计算机网络》", author: "周七", category: "计算机网络", format: "纸质书", tags: ["经典"], inStock: true, language: "中文", isbn: "978-7-00000-000-5", date: "2022-08", price: 88.00, num: 1 },
|
||||
],
|
||||
form: {
|
||||
name: "",
|
||||
author: "",
|
||||
date: "",
|
||||
price: null,
|
||||
num: 1,
|
||||
category: "",
|
||||
format: "",
|
||||
tags: [],
|
||||
inStock: false,
|
||||
language: "中文",
|
||||
isbn: ""
|
||||
},
|
||||
errorMsg: ""
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 计算总价
|
||||
sum() {
|
||||
return this.books.reduce((total, book) => {
|
||||
return total + book.price * book.num;
|
||||
}, 0);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
inc(item) {
|
||||
item.num++;
|
||||
},
|
||||
dec(item) {
|
||||
if (item.num > 1)
|
||||
item.num--;
|
||||
},
|
||||
remove(id) {
|
||||
this.books = this.books.filter(b => b.id !== id);
|
||||
},
|
||||
|
||||
// 表单验证
|
||||
validateForm() {
|
||||
if (!this.form.name.trim()) {
|
||||
this.errorMsg = "书名不能为空";
|
||||
return false;
|
||||
}
|
||||
if (!this.form.author.trim()) {
|
||||
this.errorMsg = "作者不能为空";
|
||||
return false;
|
||||
}
|
||||
if (!this.form.date) {
|
||||
this.errorMsg = "出版日期不能为空";
|
||||
return false;
|
||||
}
|
||||
if (!this.form.price || this.form.price <= 0) {
|
||||
this.errorMsg = "价格必须大于0";
|
||||
return false;
|
||||
}
|
||||
if (!this.form.num || this.form.num < 1) {
|
||||
this.errorMsg = "数量必须至少为1";
|
||||
return false;
|
||||
}
|
||||
if (!this.form.category) {
|
||||
this.errorMsg = "请选择类别";
|
||||
return false;
|
||||
}
|
||||
if (!this.form.format) {
|
||||
this.errorMsg = "请选择格式";
|
||||
return false;
|
||||
}
|
||||
this.errorMsg = "";
|
||||
return true;
|
||||
},
|
||||
|
||||
// 获取下一个ID
|
||||
nextId() {
|
||||
if (this.books.length === 0) return 1;
|
||||
return Math.max(...this.books.map(b => b.id)) + 1;
|
||||
},
|
||||
|
||||
// 添加图书到列表
|
||||
addBook() {
|
||||
if (!this.validateForm()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newBook = {
|
||||
id: this.nextId(),
|
||||
name: this.form.name,
|
||||
author: this.form.author,
|
||||
date: this.form.date,
|
||||
price: this.form.price,
|
||||
num: this.form.num,
|
||||
category: this.form.category,
|
||||
format: this.form.format,
|
||||
tags: [...this.form.tags],
|
||||
inStock: this.form.inStock,
|
||||
language: this.form.language,
|
||||
isbn: this.form.isbn
|
||||
};
|
||||
|
||||
this.books.push(newBook);
|
||||
this.resetForm();
|
||||
},
|
||||
|
||||
// 重置表单
|
||||
resetForm() {
|
||||
this.form = {
|
||||
name: "",
|
||||
author: "",
|
||||
date: "",
|
||||
price: null,
|
||||
num: 1,
|
||||
category: "",
|
||||
format: "",
|
||||
tags: [],
|
||||
inStock: false,
|
||||
language: "中文",
|
||||
isbn: ""
|
||||
};
|
||||
this.errorMsg = "";
|
||||
}
|
||||
}
|
||||
}).mount("#app");
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user