Compare commits

..

16 Commits
code ... main

21
.gitignore vendored

@ -0,0 +1,21 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

@ -0,0 +1,5 @@
{
"cSpell.words": [
"courseselected"
]
}

@ -0,0 +1,574 @@
<Card
><table ><tbody>
{ taolonglist.map((value,index)=>(
<React.Fragment >
<tr><td style={{width:"550px"}}>
<React.Fragment >
{value.lx==="wb"?<h4 style={{width:"550px"}}> {value.wb}</h4>:null}
{value.lx==="pic"? <img
src={!value.show?value.pic:value.xiugaipic}
width="550px" height="auto"/> : null}
{value.lx==="video"?<video src={!value.show? value.sp:value.xiugaisp} width="550px" height="auto" controls={true}></video>:null}
{value.lx==="download"?<a href={value.url} style={{width:"550px"}} >{value.title}</a>:null}
</React.Fragment></td>
<td ><Button onClick={e=>{
let temp=[...taolonglist]
temp[index].show=!temp[index].show;
settaolonglist(temp);
}}>修改</Button><Popconfirm
title="确认要删除此项吗"
onConfirm={event => {
let temp=[...taolonglist]
temp.splice(index,1)
settaolonglist(temp);
}}
onCancel={event => alert("no")}
okText="确认"
cancelText="不"
>
<Button onClick={e=>{
}} href="#">删除</Button>
</Popconfirm>
</td>
</tr>
{value.show ?
<tr>
<td colSpan={3}>
<React.Fragment>
<h3>编辑</h3>
<Tabs activeKey={value.leixing} onChange={e => {
let temp = [...taolonglist]
temp[index].leixing = e;
settaolonglist(temp);
}} tabBarStyle={{}} style={{width:"720px"}}>
<Tabs.TabPane tab="视频" key="video">
<table>
<tbody>
<tr>
<td>
<video src={value.xiugaisp} width="600px"
height="auto"
style={{display: "none"}}/>
</td>
</tr>
<tr>
<td>
视频路径
<Input value={value.xiugailujing}
onChange={event => {
let temp = [...taolonglist];
temp[index].xiugailujing = event.target.value;
settaolonglist(temp);
}} width="550px"
style={{width:"550px"}}
></Input>
</td>
<td><Button onClick={e => {
let temp = [...taolonglist];
temp[index].xiugaisp = temp[index].xiugailujing;
settaolonglist(temp);
}}>打开</Button></td>
</tr>
<tr>
<td>
<Upload
name='file'
action={HOUDUAN+'api/uploadtm'}
headers={{
th: value.uuid,
xx: fujianuuid,
lx: "kaoshi"
}}
onChange={
(info) => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
//setTms(uuid.v4())
message.success(`${info.file.name} file uploaded successfully`);
_.delay(() => {
console.log('Done after 1 second');
const temp = [...taolonglist];
temp[index].xiugaisp = ZHIYUE+ value.uuid + "/" + fujianuuid + path.extname(info.file.name);
settaolonglist(temp);
setFujianuuid(uuid.v4())
//settaolonglist(uuid.v4())
}, 200);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
}
}
showUploadList={false}><Button>
<Icon type="upload"/>上传附件
</Button>
</Upload>
</td>
</tr>
<tr>
<td>
<Button onClick={e => {
let temp = [...taolonglist];
temp[index].sp = temp[index].xiugaisp;
temp[index].lx = temp[index].leixing
settaolonglist(temp);
}}>确认</Button>
</td>
</tr>
</tbody>
</table>
</Tabs.TabPane>
<Tabs.TabPane tab="图片" key="pic">
<table>
<tbody>
<tr>
<td>
<img src={value.xiugaipic} width="600px"
height="auto"
style={{display: "none"}}/>
</td>
</tr>
<tr>
<td>
<span>图片路径
<Input value={value.xiugailujing} onChange={event => {
let temp = [...taolonglist];
temp[index].xiugailujing = event.target.value;
settaolonglist(temp);
}}
width="550px"
style={{width:"550px"}}
></Input></span>
</td>
<td><Button onClick={e => {
let temp = [...taolonglist];
temp[index].xiugaipic = temp[index].xiugailujing;
settaolonglist(temp);
//settaolonglist(uuid.v4())
}}>打开</Button></td>
</tr>
<tr>
<td>
<Upload
name='file'
action={HOUDUAN+'api/uploadtm'}
headers={{
th: value.uuid,
xx: fujianuuid,
lx: "kaoshi"
}}
onChange={
(info) => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
//setTms(uuid.v4())
message.success(`${info.file.name} file uploaded successfully`);
_.delay(() => {
console.log('Done after 1 second');
const temp = [...taolonglist];
temp[index].xiugaipic = ZHIYUE+value.uuid + "/" + fujianuuid + path.extname(info.file.name);
settaolonglist(temp);
setFujianuuid(uuid.v4());
}, 200);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
}
}
showUploadList={false}><Button>
<Icon type="upload"/>上传附件
</Button>
</Upload>
</td>
</tr>
<tr>
<td><Button onClick={e => {
let temp = [...taolonglist];
temp[index].pic = temp[index].xiugaipic;
temp[index].lx = temp[index].leixing
settaolonglist(temp);
}}>保存</Button></td>
</tr>
</tbody>
</table>
</Tabs.TabPane>
<Tabs.TabPane tab="附件" key="download">
<table>
<tbody>
<tr>
<td>文件标题</td>
<td style={{width: "400px"}}>
<Input value={value.xiugai} onChange={e => {
let temp = [...taolonglist];
temp[index].xiugai = e.target.value;
settaolonglist(temp);
}}
width="550px"
style={{width:"550px"}}
></Input>
</td>
</tr>
<tr>
<td>
文件路径
</td>
<td>
<Input value={value.xiugaiwj}
onChange={event => {
let temp = [...taolonglist];
temp[index].xiugaiwj = event.target.value;
temp[index].lx = temp[index].leixing
settaolonglist(temp);
}}
width="550px"
style={{width:"550px"}}
></Input></td>
</tr>
<tr>
<td>
<Upload
name='file'
action={HOUDUAN+'api/uploadtm'}
headers={{
th: value.uuid,
xx: fujianuuid,
lx: "kaoshi"
}}
onChange={
(info) => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
//setTms(uuid.v4())
message.success(`${info.file.name} file uploaded successfully`);
_.delay(() => {
console.log('Done after 1 second');
const temp = [...taolonglist];
temp[index].xiugaiwj = ZHIYUE+value.uuid + "/" + fujianuuid + path.extname(info.file.name);
temp[index].xiugai = info.file.name;
settaolonglist(temp);
setFujianuuid(uuid.v4())
}, 200);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
}
}
showUploadList={false}><Button>
<Icon type="upload"/>上传附件
</Button>
</Upload>
<Button onClick={e => {
let temp = [...taolonglist];
temp[index].url = temp[index].xiugaiwj;
temp[index].title = temp[index].xiugai;
temp[index].lx = temp[index].leixing
settaolonglist(temp);
}}>保存</Button>
</td>
</tr>
</tbody>
</table>
</Tabs.TabPane>
<Tabs.TabPane tab="文字" key="wb">
<Input.TextArea value={value.xiugaiwb}
onChange={event => {
let temp = [...taolonglist];
temp[index].xiugaiwb = event.target.value;
temp[index].lx = temp[index].leixing
settaolonglist(temp);
}} width="550px"
style={{width:"550px"}}
></Input.TextArea>
<Button onClick={e => {
let temp = [...taolonglist]
temp[index].wb = temp[index].xiugaiwb;
settaolonglist(temp);
}}>修改</Button>
</Tabs.TabPane>
</Tabs>
</React.Fragment>
</td>
</tr>
:null}
</React.Fragment>
))}
</tbody></table></Card>
<Card>
<Tabs activeKey={add.leixing} onChange={e=>{
let temp={...add};
temp.leixing=e;
setAdd(temp)
}}>
<Tabs.TabPane tab="视频" key="video">
<table><tbody>
<tr ><td >
<video src={add.xiugaisp} width="600px" height="auto" controls={true}/>
</td>
</tr>
<tr><td>
<Upload
name='file'
action={HOUDUAN+'api/uploadtm'}
headers={{
th: add.uuid ,
xx: fujianuuid,
lx: "kaoshi"
}}
onChange={
(info) => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
//setTms(uuid.v4())
message.success(`${info.file.name} file uploaded successfully`);
_.delay(() => {
console.log('Done after 1 second');
const temp={...add};
temp.xiugaisp=ZHIYUE+add.uuid+"/"+fujianuuid + path.extname(info.file.name);
setAdd(temp);
setFujianuuid(uuid.v4())
}, 200);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
}
}
showUploadList={false}><Button>
<Icon type="upload"/>上传附件
</Button>
</Upload>
</td></tr>
<tr>
<td>文件路径
<Input value={add.url} width="400px" onChange={event => {
let temp={...add};
temp.url=event.target.value;
setAdd(temp);
}}></Input>
<Button onClick={e=>{
let temp={...add};
temp.xiugaisp=temp.url;
setAdd(temp);
}
}>应用</Button>
</td>
</tr>
</tbody></table>
</Tabs.TabPane>
<Tabs.TabPane tab="图片" key="pic">
<tr>
<td>
<img src={add.xiugaipic} width="600px" height="auto"/>
</td>
</tr>
<tr>
<td>
<Upload
name='file'
action={HOUDUAN+'api/uploadtm'}
headers={{
th: add.uuid,
xx: fujianuuid,
lx: "kaoshi"
}}
onChange={
(info) => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
//setTms(uuid.v4())
message.success(`${info.file.name} file uploaded successfully`);
_.delay(() => {
console.log('Done after 1 second');
const temp = {...add};
temp.xiugaipic =ZHIYUE+ add.uuid + "/" + fujianuuid + path.extname(info.file.name);
setAdd(temp);
setFujianuuid(uuid.v4())
}, 200);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
}
}
showUploadList={false}><Button>
<Icon type="upload"/>上传附件
</Button>
</Upload>
</td>
</tr>
<tr>
<td>文件路径
<Input value={add.url} width="400px" onChange={event => {
let temp = {...add};
temp.url = event.target.value;
setAdd(temp);
}}></Input>
<Button onClick={e => {
let temp = {...add};
temp.xiugaipic = temp.url;
setAdd(temp);
}
}>应用</Button>
</td>
</tr>
</Tabs.TabPane>
<Tabs.TabPane tab="附件" key="download">
<tr>
<td><a href={add.xiugailujing}>{add.xiugai}</a></td>
</tr>
<tr>
<td>文件标题</td>
<td >
<Input value={add.xiugai} onChange={event => {
let temp={...add}
temp.xiugai=event.target.value;
setAdd(temp);
}}></Input>
</td>
</tr>
<tr>
<td>文件路径</td>
<td style={{width:"350px"}}>
<Input value={add.xiugailujing} onChange={event => {
let temp={...add};
temp.xiugailujing=event.target.value;
setAdd(temp);
}}></Input></td>
</tr>
<tr>
<td>
<Upload
name='file'
action={HOUDUAN+'api/uploadtm'}
headers={{
th: add.uuid ,
xx: fujianuuid,
lx: "kaoshi"
}}
onChange={
(info) => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
//setTms(uuid.v4())
message.success(`${info.file.name} file uploaded successfully`);
_.delay(() => {
console.log('Done after 1 second');
const temp = {...add};
temp.xiugai=info.file.name;
temp.xiugailujing = ZHIYUE+ add.uuid + "/" + fujianuuid + path.extname(info.file.name)
setAdd(temp)
setFujianuuid(uuid.v4());
}, 200);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
}
}
showUploadList={false}><Button>
<Icon type="upload"/>上传附件
</Button>
</Upload>
</td></tr>
</Tabs.TabPane>
<Tabs.TabPane tab="文字" key="wb">
<Input.TextArea value={add.xiugaiwb} onChange={event => {
let temp={...add};
temp.xiugaiwb=event.target.value;
setAdd(temp);
}} style={{height:"250px"}}></Input.TextArea>
</Tabs.TabPane>
</Tabs>
<Button onClick={
// {value.lx==="wb"?<h4>{value.wb}</h4>:null}
// {value.lx==="pic"? <img
// src={value.pic}
// width="600px" height="auto"/> : null}
// {value.lx==="video"?<video src={value.sp} width="600px" height="auto" controls={true}></video>:null}
// {value.lx==="download"?<a href={value.url}>{value.title}</a>:null}
e=>{
const xiang={
lx:add.leixing,
leixing:add.leixing,
wb:add.xiugaiwb,
xiugaiwb:add.xiugaiwb,
pic:add.xiugaipic,
xiugaiwj:add.xiugailujing,//修改附件路径
xiugaipic:add.xiugaipic,
sp:add.xiugaisp,
xiugaisp:add.xiugaisp,
xiugailujing:add.xiugailujing,
url:add.xiugailujing,
xiugai:add.xiugai,
title:add.xiugai,
show:false
}
let temp=[...taolonglist]
temp.push(xiang);
settaolonglist(temp);
const addtemp={leixing:add.leixing,uuid:uuid.v4()}
setAdd(addtemp);
}
}>
添加
</Button>
</Card>

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

12015
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,80 @@
{
"name": "uni-preset-vue",
"version": "0.0.0",
"scripts": {
"dev:custom": "uni -p",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:mp-xhs": "uni -p mp-xhs",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-jd": "uni build -p mp-jd",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:mp-xhs": "uni build -p mp-xhs",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"type-check": "vue-tsc --noEmit"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-4030620241128001",
"@dcloudio/uni-app-harmony": "3.0.0-4030620241128001",
"@dcloudio/uni-app-plus": "3.0.0-4030620241128001",
"@dcloudio/uni-components": "3.0.0-4030620241128001",
"@dcloudio/uni-h5": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-alipay": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-baidu": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-jd": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-lark": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-qq": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-toutiao": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-weixin": "3.0.0-4030620241128001",
"@dcloudio/uni-mp-xhs": "3.0.0-4030620241128001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4030620241128001",
"@dcloudio/uni-ui": "^1.5.7",
"@hyoga/uni-socket.io": "^3.0.4",
"axios": "^1.9.0",
"axios-adapter-uniapp": "^0.1.4",
"dayjs": "^1.11.13",
"moment": "^2.30.1",
"pinia": "^3.0.2",
"uuid": "^11.1.0",
"vue": "^3.4.21",
"vue-i18n": "^9.1.9",
"vue-router": "^4.5.1"
},
"devDependencies": {
"@dcloudio/types": "^3.4.8",
"@dcloudio/uni-automator": "3.0.0-4030620241128001",
"@dcloudio/uni-cli-shared": "3.0.0-4030620241128001",
"@dcloudio/uni-stacktracey": "3.0.0-4030620241128001",
"@dcloudio/vite-plugin-uni": "3.0.0-4030620241128001",
"@vue/runtime-core": "^3.4.21",
"@vue/tsconfig": "^0.1.3",
"rollup-plugin-visualizer": "^6.0.1",
"sass": "^1.69.0",
"sass-loader": "^10.1.1",
"typescript": "^4.9.4",
"vite": "5.2.8",
"vue-tsc": "^1.0.24"
}
}

@ -0,0 +1,7 @@
{
"pages": [
// ... existing code ...
// ... existing code ...
]
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,46 @@
import fs from "fs";
import child_process from "node:child_process";
app.post("/apistu/pppta",async (req,res)=>{
const sqlread="SELECT * FROM tmj_ex WHERE id=?"
const resultptainfor=await new Promise((resolve, reject) => {
connection.query(sqlread,[req.body.uuid],(error,results,fields)=>{
resolve(results[0]);
})
})
let information=JSON.parse(resultptainfor.tmlist)[parseInt(req.body.th)]
res.send(JSON.parse(information.information))
let tmlist=JSON.parse(information.information).testprint
let uuid1=uuidv4.v4()
let worktemp="C:\\Users\\l\\Music\\reat\\src\\MarkdownEditor\\test\\"+uuid1
fs.mkdirSync(worktemp)
if(req.body.pl==="java") {
fs.writeFileSync(worktemp + "\\Main.java", req.body.code)
fs.writeFileSync(worktemp + "\\inputby.txt", "work")
tmlist.map((tmone, index) => {
fs.writeFileSync(worktemp + "\\input" + (index + 1) + ".txt", tmone)
})
child_process.execFileSync(`python`, ["C:\\Users\\l\\PycharmProjects\\PythonProject7\\py2.py", worktemp, "java", 1])
let json_wb = fs.readFileSync(worktemp + "\\zt.json").toString()
let json_zt = JSON.parse(json_wb)
let zt_1 = [...json_zt.test]
if (json_zt.by === 0) {
let work_result = [...tmlist]
tmlist.map((tmresult, indexresult) => {
zt_1[index].by = 0
zt_1[index].sc = fs.readFileSync(worktemp + "\\output1.txt", {encoding: "utf8"})
zt_1[index].err = fs.readFileSync(worktemp + "\\err1.txt", {encoding: "utf8"})
zt_1[index] = {...zt_1[index], ...tmresult}
})
//zt_1.by=0
//zt_1.sc=fs.readFileSync(worktemp+"\\output1.txt",{encoding:"utf8"})
//zt_1.err=fs.readFileSync(worktemp+"\\err1.txt",{encoding:"utf8"})
}else {
let by_err={}
by_err.by=json_zt.by
by_err.err=fs.readFileSync(worktemp+"\\err_by.txt",{encoding:"utf8"})
}
}
})

@ -0,0 +1 @@
[{"lx":"单选题","uuid":"e6d957c8-7d7b-4df1-999f-7959e776317a","xuexiang":[{"lx":"wb","wb":"选项A 正确","pic":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fddd83bc0-4c41-482a-8888-083d09096ae9%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1739691239&t=f2f6f3c92693182304a737e6faf0e97d"},{"lx":"wb","wb":"选项B","pic":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fddd83bc0-4c41-482a-8888-083d09096ae9%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1739691239&t=f2f6f3c92693182304a737e6faf0e97d"}],"tm":{"lx":"pic","pic":"http://localhost:89/e6d957c8-7d7b-4df1-999f-7959e776317a/askforteakcb.png","wb":"test"},"daan":"A"},{"lx":"多选题","uuid":"9bc5fe7b-9d41-42ff-8e3d-a37154e2fad1","xuexiang":[{"lx":"wb","wb":"选项A 正确","pic":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fddd83bc0-4c41-482a-8888-083d09096ae9%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1739691239&t=f2f6f3c92693182304a737e6faf0e97d"},{"lx":"wb","wb":"选项B 正确","pic":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fddd83bc0-4c41-482a-8888-083d09096ae9%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1739691239&t=f2f6f3c92693182304a737e6faf0e97d"},{"lx":"wb","wb":"选项C","pic":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fddd83bc0-4c41-482a-8888-083d09096ae9%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1739691239&t=f2f6f3c92693182304a737e6faf0e97d"}],"tm":{"lx":"pic","pic":"http://localhost:89/9bc5fe7b-9d41-42ff-8e3d-a37154e2fad1/askforteakcb.png","wb":""},"daan":["A","B"]},{"lx":"填空题","uuid":"1b100b1b-ebaa-469c-bd2d-3227760cb32f","xuexiang":[],"tm":{"lx":"wb","pic":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fddd83bc0-4c41-482a-8888-083d09096ae9%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1739691239&t=f2f6f3c92693182304a737e6faf0e97d","wb":"填空题 答案为TEST"},"sr":"TEST"},{"lx":"填空题","uuid":"91e4ffa9-21e2-47f0-9480-6c0d6ffc41dc","xuexiang":[],"tm":{"lx":"wb","pic":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fddd83bc0-4c41-482a-8888-083d09096ae9%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1739691239&t=f2f6f3c92693182304a737e6faf0e97d","wb":"填空题 由老师判"},"sr":"pick up me 满分"},{"lx":"简答题","uuid":"8f577885-a8a4-43cf-94d4-b3a50828275a","xuexiang":[],"tm":{"lx":"wb","pic":"https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2Fddd83bc0-4c41-482a-8888-083d09096ae9%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1739691239&t=f2f6f3c92693182304a737e6faf0e97d","wb":"简答题 老师判"},"filelist":[{"url":"8f577885-a8a4-43cf-94d4-b3a50828275a202413501062/0ab5b8c5-1cdb-4531-b178-cc6ba74737e1.png","file":"a8.png"}]}]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,124 @@
const fs = require('fs');
const path = require('path');
const uuidv4 = require('uuid');
var q = require('qiao-zip');
const os = require("os");
/*
name:biao[j][nameid],
xuehao:biao[j][xuehaoid],
sex:biao[j][sex],
classid:biao[j][classid],
xueyuan:biao[j][xueyaunid],
idcard:biao[j][idcardid],
ruxuetime:ruxue,
iphone:biao[j][iphone],
parentphone:biao[j][parentphone],
parentname:biao[j][parentname],
*/
function nametop(){
}
async function makeExcel(exceljson) {
let wblist=[]//excel文本列表
wblist.push("姓名");
wblist.push("学号")
wblist.push("性别")
wblist.push("专业");
wblist.push("学院")
wblist.push("身份证号")
wblist.push("入学时间")
wblist.push("手机号")
wblist.push("默认密码")
let listexcel=""
//console.log(exceljson);
exceljson.map((value,index) => {
let nameindex = wblist.indexOf(value.name);
if (nameindex == -1) {
wblist.push(value.name);
nameindex = wblist.indexOf(value.name);
}
let namexml = `<c r="A${index + 2}" t="s"><v>${nameindex}</v></c> `;
let xuehaoindex
= wblist.indexOf(value.xuehao);
if (xuehaoindex === -1) {
wblist.push(value.xuehao);
xuehaoindex = wblist.indexOf(value.xuehao);
}
let xuehaoxml = `<c r="B${index + 2}" t="s"><v>${xuehaoindex}</v></c> `
let sexindex = wblist.indexOf(value.sex);
if (sexindex === -1) {
wblist.push(value.sex)
sexindex = wblist.indexOf(value.sex)
}
let sexxml = `<c r="C${index + 2}" t="s"><v>${sexindex}</v></c>`
let classindex = wblist.indexOf(value.classid);
if (classindex === -1) {
wblist.push(value.classid);
classindex = wblist.indexOf(value.classid);
}
let classxml = `<c r="D${index + 2}" t="s"><v>${classindex}</v></c>`
let xueyuanindex = wblist.indexOf(value.xueyuan);
if (xueyuanindex === -1) {
wblist.push(value.xueyuan);
xueyuanindex = wblist.indexOf(value.xueyuan);
}
let xueyuanxml = `<c r="E${index + 2}" t="s"><v>${xueyuanindex}</v></c>`
let idcardindex = wblist.indexOf(value.idcard);
if (idcardindex === -1) {
wblist.push(value.idcard);
idcardindex = wblist.indexOf(value.idcard);
}
let idcardxml = `<c r="F${index + 2}" t="s"><v>${idcardindex}</v></c>`
let ruxuetimeindex = wblist.indexOf(value.ruxuetime);
if (ruxuetimeindex === -1) {
wblist.push(value.ruxuetime);
ruxuetimeindex = wblist.indexOf(value.ruxuetime);
}
let ruxuetimexml = `<c r="G${index + 2}" t="s"><v>${ruxuetimeindex}</v></c>`
let iphoneindex = wblist.indexOf(value.iphone);
if (iphoneindex === -1) {
wblist.push(value.iphone);
iphoneindex = wblist.indexOf(value.iphone);
}
let iphonexml = `<c r="H${index + 2}" t="s"><v>${iphoneindex}</v></c>`
let passwordindex = wblist.indexOf(value.password);
console.log(value.password)
if (passwordindex === -1) {
wblist.push(value.password);
passwordindex = wblist.indexOf(value.password);
}
let passwordxml = `<c r="I${index + 2}" t="s"><v>${passwordindex}</v></c>`
const xmlall=namexml+xuehaoxml+sexxml+classxml+xueyuanxml+idcardxml+ruxuetimexml+iphonexml+passwordxml;
const row=`<row r = "${index+2}" spans = "1:9" >${xmlall}</row> `
listexcel+=row;
})
let moban=fs.readFileSync("sheet2.xml","utf8")
let shuzu= moban.split("shuliang");
moban=shuzu[0]+`I${exceljson.length+1}`+shuzu[1];
shuzu=moban.split("charuwengjian");
moban=shuzu[0]+listexcel+shuzu[1];
let stringlist="";
wblist.map((value,index)=>{
const wbxml=`<si><t>${value}</t></si>`
stringlist+=wbxml;
})
let wbxml=fs.readFileSync("sharedStrings.xml","utf8")
let wbshuzu=wbxml.split("wblist");
wbxml=wbshuzu[0]+stringlist+wbshuzu[1];
const cacheDir = path.join(os.homedir(), '.cache');
const filedir=cacheDir+'\\learn\\'+uuidv4.v4();
console.log(filedir)
fs.mkdirSync(filedir);
await q.unzip("scexcelstu.zip", filedir)
fs.writeFileSync(`${filedir}\\xl\\sharedStrings.xml`,wbxml)
fs.writeFileSync(`${filedir}\\xl\\worksheets\\sheet1.xml`,moban)
const uuidv41=uuidv4.v4();
const dir=`C:\\Users\\l\\WebstormProjects\\examp\\serve\\nginx\\html\\stuexcel\\${uuidv41}.zip`;
await q.zip(filedir, dir,false)
fs.copyFileSync(dir,`C:\\Users\\l\\WebstormProjects\\examp\\serve\\nginx\\html\\stuexcel\\${uuidv41}.xlsx`)
return(`http://localhost:89/stuexcel/${uuidv41}.xlsx`);
}
exports.makeExcel=makeExcel;

@ -0,0 +1,59 @@
const uuidv4 = require('uuid');
const express = require('express');
const multer = require('multer');
const cors = require('cors');
const app = express();
const fs = require('fs');
const zlib = require('zlib');
const tar = require('tar');
const axios = require('axios');
const { promisify } = require('util');
const {exec} = require('child_process');
const moment=require('moment');
var mysql = require('mysql');
var q = require('qiao-zip');
const { basename, join, extname} = require("node:path");
const {c, r} = require("tar");
const {response} = require("express");
const {homedir} = require("node:os");
const readline = require("node:readline");
const crypto = require('crypto');
const makeexcel=require('./makeexcel')
const speakeasy = require('speakeasy');
const server=require('http').Server(app);
const io=require('socket.io')(server,{
pingInterval: 10000,
pingTimeout: 5000,
allowEIO3: true, // 如果你使用的是 Socket.io 3.x 或更高版本
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"]
},
serveClient: false,
maxHttpBufferSize: 1e9, // 10 MB
perMessageDeflate: {
threshold: 1024
}
});
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '416416',
database : 'neigongda'
});
connection.connect();
app.use(cors({
origin: ['http://localhost:3000','http://localhost:89'],
}));
app.use(express.urlencoded({extended: false})); // 现在就方便多了express的两个方法一执行就行啦
app.use(express.json({limit: '100mb', extended: true}));
const port = process.env.PORT || 3400;
app.get('/api/test',(req,res)=>{
res.send("test");
})
server.listen(3500);

@ -0,0 +1,33 @@
{
"name": "serve",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"private": true,
"dependencies": {
"@napi-rs/canvas": "^0.1.70",
"archiver": "^7.0.1",
"axios": "^1.7.9",
"cors": "^2.8.5",
"express": "^4.21.1",
"http": "^0.0.1-security",
"lz-string": "^1.5.0",
"moment": "^2.30.1",
"multer": "^1.4.5-lts.1",
"mysql": "^2.18.1",
"node-xlsx": "^0.24.0",
"nodemon": "^3.1.9",
"pdfjs-dist": "^3.10.111",
"qiao-zip": "^4.7.4",
"qrcode": "^1.5.4",
"socket.io": "^4.8.1",
"speakeasy": "^2.0.0",
"tar": "^7.4.3",
"uuid": "^11.0.3",
"xlsx": "^0.18.5",
"xml2js": "^0.6.2"
}
}

@ -0,0 +1,140 @@
async function panduanyuejuan(){
const sqlstd="SELECT * FROM kaoshipaper WHERE uuid=?";
const resultstd=await new Promise((resolve, reject) => {
connection.query(sqlstd,[req.query.ksh],(error, results, fields) => {
if(error)throw error;
resolve(results[0]);
})
})
if(JSON.parse(resultstd.xx).pan_jue_finsih===false) {
const sql = "SELECT * FROM kaoshiend WHERE ks=?";
const resultksdaan = await new Promise((resolve, reject) => {
connection.query(sql, [parseInt(req.query.ksh)], (error, results, fields) => {
if (error) throw error;
resolve(results);
})
})
let finish = true;
for (let i = 0; i < resultksdaan.length; i++) {
const ksdaan = JSON.parse(resultksdaan[i].ksdaan);
ksdaan.map((value, index) => {
if (value.lx === "填空题") {
console.log(resultstd)
const stddaan = JSON.parse(resultstd.std);
if (stddaan[i].panjaun === "teacher") {
if (value.teacherid === undefined) {
finish = false
}
}
}
if (value.lx === "简答题") {
if (value.teacherid === undefined) {
finish = false
}
}
})
}
if(finish===true){
let jsonxx=JSON.parse(resultstd.xx);
jsonxx.pan_jue_finsih=true
const sqlupdate = 'UPDATE kaoshipaper SET xx=? WHERE `uuid`=?';
const up = await new Promise((resolve, reject) => {
connection.query(sqlupdate, [JSON.stringify(jsonxx, null, 4), req.query.ksh], (error, results, fields) => {
if (error) throw error;
resolve(results);
})
})
}
let infor=[]
let allprint=[]
if(finish===true){
for (let i = 0; i < resultksdaan.length; i++) {
const ksdaan = JSON.parse(resultksdaan[i].ksdaan);
let num=0;
let ksdaantemp=[...ksdaan]
ksdaan.map((value, index) => {
if (value.lx === "填空题") {
console.log(resultstd)
const stddaan = JSON.parse(resultstd.std);
if (stddaan[index].panjaun === "teacher") {
num=num+value.teacherprint
ksdaantemp[index].way="teacher"
ksdaantemp[index].printstu=value.teacherprint
}else{
if(value.sr.trimStart()===stddaan[index].std.trimStart()){
num=num+stddaan[index].print
ksdaantemp[index].way="computer"
ksdaantemp[index].printstu=stddaan[index].print
}
}
}
else if (value.lx === "简答题") {
const stddaan = JSON.parse(resultstd.std);
num=num+value.teacherprint
//ksdaantemp[index].way="teacher"
ksdaantemp[index].printstu=value.teacherprint
}else if(value.lx==="多选题"){
let zq=true
const stddaan = JSON.parse(resultstd.std);
value.daan.map((item,xuaho)=>{
if(!stddaan[index].std.includes(item)){}
zq=false
})
if(value.daan.length!==stddaan[index].std.length){
zq=false
}
if(zq){
num=num+stddaan[index].print
//ksdaantemp[index].way="teacher"
ksdaantemp[index].printstu=stddaan[index].print
}else{
ksdaantemp[index].printstu=0
}
}else if(value.lx==="单选题"){
const stddaan = JSON.parse(resultstd.std);
if(value.daan===stddaan[index].std){
num=num+stddaan[index].print
//ksdaantemp[index].way="teacher"
ksdaantemp[index].printstu=stddaan[index].print
}else{
ksdaantemp[index].printstu=0
num=num+0
}
}else if(value.lx==="判断题"){
const stddaan = JSON.parse(resultstd.std);
if(value.daan===stddaan[index].std){
num=num+stddaan[index].print
//ksdaantemp[index].way="teacher"
ksdaantemp[index].printstu=stddaan[index].print
}else{
ksdaantemp[index].printstu=0
}
}
})
const sqlupdate = 'UPDATE kaoshiend SET ksdaan=?,allprint=? WHERE id=?';
//resultksdaan[i].
await new Promise((resolve, reject) => {
connection.query(sqlupdate,[JSON.stringify(ksdaantemp,null,4),num,resultksdaan[i].id],(error, results) => {
if (error) throw error;
resolve(results);
})
})
allprint.push(num)
infor.push({infor:ksdaantemp,num:num})
}
}
res.send({finish: finishr})
}else{
res.send({finish: true})
}
}

@ -0,0 +1,165 @@
const xml2js = require('xml2js');
const parser = new xml2js.Parser();
const fs = require('fs');
const moment = require('moment');
const os = require('os');
const path = require('path');
var q = require('qiao-zip');
const uuidv4 = require('uuid');
const sheetlist=async(xlsx)=>{
const cacheDir = path.join(os.homedir(), '.cache');
const filedir=cacheDir+'\\learn\\'+uuidv4.v4();
fs.mkdirSync(filedir);
await q.unzip(xlsx, filedir)
const xmlwb=fs.readFileSync(filedir+"/[Content_Types].xml",'utf8');
const wbresult=await new Promise((resolve,reject)=>{
parser.parseString(xmlwb, (err, result) => {
if (err) {
console.error(err);
throw err;
}
resolve(result);
});
})
//console.log(wbresult);
let slist=[];
let wblist=await wblsit(filedir+"/xl/sharedStrings.xml")
wbresult.Types.Override.map((value,index)=>{
//console.log(value);
if(value['$'].ContentType==="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"){
slist.push(value['$'].PartName);
}
})
let chengji=[];
//console.log(slist);
let chengname=[];
for(let i=0;i<slist.length;i++){
const biao=await wjb(filedir+slist[i],wblist);
//
let xiangnum=-1;//姓名项在第几列
for(let j=0;j<biao.length;j++){
if(biao[j].includes("姓名")){
xiangnum=j;
break;
}
}
let nameid=biao[xiangnum].indexOf("姓名");
let xuehaoid=biao[xiangnum].indexOf("学号");
let sex=biao[xiangnum].indexOf("性别");
let classid=biao[xiangnum].indexOf("班级");
let xueyaunid=biao[xiangnum].indexOf("学院");
let idcardid=biao[xiangnum].indexOf("身份证号");
let ruxuetime=biao[xiangnum].indexOf("入学日期")
let iphone=biao[xiangnum].indexOf("学生手机号");
let parentname=biao[xiangnum].indexOf("家长姓名")
let parentphone=biao[xiangnum].indexOf("家长手机号")
//console.log(nameid)
// console.log(xiangnum);
//console.log(biao);
const stulsit=[];
for(let j=xiangnum+1;j<biao.length;j++){
let ruxue="" ;
if( /^\d+$/.test(biao[j][ruxuetime])){
if (parseInt(biao[j][ruxuetime])<80000){
ruxue=moment("1900-01-01").add(parseInt(biao[j][ruxuetime])-2,'day').format();
//console.log(mom);
}
}else{
ruxue=moment(biao[j][ruxuetime]).format();
}
const stu={
name:biao[j][nameid],
xuehao:biao[j][xuehaoid],
sex:biao[j][sex],
classid:biao[j][classid],
xueyuan:biao[j][xueyaunid],
idcard:biao[j][idcardid],
ruxuetime:ruxue,
iphone:biao[j][iphone],
parentphone:biao[j][parentphone],
parentname:biao[j][parentname],
choose:true
}
// console.log(stu);
stulsit.push(stu);
}
chengji.push({stu:stulsit,name:path.parse(filedir+slist[i]).name,add:false});
chengname.push(path.parse(filedir+slist[i]).name);
}
//
// console.log(chengname);
// console.log(chengji);
return chengji;
}
const wblsit=async (file)=>{
const xmlwb=fs.readFileSync(file,'utf8');
// console.log(xmlwb);
const wbresult=await new Promise((resolve,reject)=>{
parser.parseString(xmlwb, (err, result) => {
if (err) {
console.error(err);
throw err;
}
resolve(result);
});
})
let wbone=[];
wbresult.sst.si.map((value)=>{
//console.log(value);
wbone.push(value.t[0]);
})
//console.log(wbone);
return wbone;
}
const wjb=async(file,list)=>{
const xmlwb=fs.readFileSync(file,'utf8');
const wbresult=await new Promise((resolve,reject)=>{
parser.parseString(xmlwb, (err, result) => {
if (err) {
console.error(err);
throw err;
}
resolve(result);
});
})
const excerow=wbresult.worksheet.sheetData[0].row;
let allexcel=[]
wbresult.worksheet.sheetData[0].row.map((value,index)=>{
let hangexcel=[];
value.c.map((value,index)=>{
//console.log(value);
const t=value['$'].t;
const v=value.v[0];
let zhi="";
// if (parseInt(v)<40000&&parseInt(v)>80000) {
if (t === undefined) {
zhi = v;
} else if (t === 's') {
zhi = list[parseInt(v)];
} else {
zhi = v;
}
// }else{
// zhi =moment("1900-01-01").add(parseInt(v),'days').format('YYYY-MM-DD');
//}
hangexcel.push(zhi);
})
allexcel.push(hangexcel);
});
// console.log(allexcel);
return allexcel;
}
//wblsit("C:/Users/l/Desktop/a1/xl/sharedStrings.xml").then(t=>{
// wjb("C:/Users/l/Desktop/a1/xl/worksheets/sheet1.xml",t)
//})
//sheetlist("C:\\Users\\l\\Desktop\\12\\a141.xlsx")
const cacheDir = path.join(os.homedir(), '.cache');
console.log(cacheDir);
exports.sheetlist=sheetlist;

@ -0,0 +1,109 @@
//生成课表
const fs = require('fs');
const LkbString = require('lz-string');
const kb=[];
function compressText(text) {
return LkbString.compressToEncodedURIComponent(text);
}
for (let i = 0; i < 11; i++) {
kb[i*28+0]={ke:1,time:"n"+i.toString()+"z1A1"};
kb[i*28+1]={ke:2,time:"n"+i.toString()+"z1A2"};
kb[i*28+2]={ke:0,time:"n"+i.toString()+"z1A3"};
kb[i*28+3]={ke:0,time:"n"+i.toString()+"z1A4"};
kb[i*28+4]={ke:0,time:"n"+i.toString()+"z2A1"};
kb[i*28+5]={ke:2,time:"n"+i.toString()+"z2A2"};
kb[i*28+6]={ke:3,time:"n"+i.toString()+"z2A3"};
kb[i*28+7]={ke:0,time:"n"+i.toString()+"z2A4"};
kb[i*28+8]={ke:1,time:"n"+i.toString()+"z3A1"};
kb[i*28+9]={ke:4,time:"n"+i.toString()+"z3A2"};
kb[i*28+10]={ke:0,time:"n"+i.toString()+"z3A3"};
kb[i*28+11]={ke:0,time:"n"+i.toString()+"z3A4"};
kb[i*28+12]={ke:1,time:"n"+i.toString()+"z4A1"};
kb[i*28+13]={ke:2,time:"n"+i.toString()+"z4A2"};
kb[i*28+14]={ke:3,time:"n"+i.toString()+"z4A3"};
kb[i*28+15]={ke:0,time:"n"+i.toString()+"z4A4"};
kb[i*28+16]={ke:3,time:"n"+i.toString()+"z5A1"};
kb[i*28+17]={ke:4,time:"n"+i.toString()+"z5A2"};
kb[i*28+18]={ke:5,time:"n"+i.toString()+"z5A3"};
kb[i*28+19]={ke:0,time:"n"+i.toString()+"z5A4"};
kb[i*28+20]={ke:0,time:"n"+i.toString()+"z6A1"};
kb[i*28+21]={ke:0,time:"n"+i.toString()+"z6A2"};
kb[i*28+22]={ke:0,time:"n"+i.toString()+"z6A3"};
kb[i*28+23]={ke:0,time:"n"+i.toString()+"z6A4"};
kb[i*28+24]={ke:0,time:"n"+i.toString()+"z7A1"};
kb[i*28+25]={ke:0,time:"n"+i.toString()+"z7A2"};
kb[i*28+26]={ke:0,time:"n"+i.toString()+"z7A3"};
kb[i*28+27]={ke:0,time:"n"+i.toString()+"z7A4"};
}
for (let i = 11; i < 15; i++) {
kb[i*28+0]={ke:1,time:"n"+i.toString()+"z1A1"};
kb[i*28+1]={ke:2,time:"n"+i.toString()+"z1A2"};
kb[i*28+2]={ke:0,time:"n"+i.toString()+"z1A3"};
kb[i*28+3]={ke:6,time:"n"+i.toString()+"z1A4"};
kb[i*28+4]={ke:0,time:"n"+i.toString()+"z2A1"};
kb[i*28+5]={ke:2,time:"n"+i.toString()+"z2A2"};
kb[i*28+6]={ke:3,time:"n"+i.toString()+"z2A3"};
kb[i*28+7]={ke:0,time:"n"+i.toString()+"z2A4"};
kb[i*28+8]={ke:1,time:"n"+i.toString()+"z3A1"};
kb[i*28+9]={ke:4,time:"n"+i.toString()+"z3A2"};
kb[i*28+10]={ke:0,time:"n"+i.toString()+"z3A3"};
kb[i*28+11]={ke:0,time:"n"+i.toString()+"z3A4"};
kb[i*28+12]={ke:1,time:"n"+i.toString()+"z4A1"};
kb[i*28+13]={ke:2,time:"n"+i.toString()+"z4A2"};
kb[i*28+14]={ke:3,time:"n"+i.toString()+"z4A3"};
kb[i*28+15]={ke:0,time:"n"+i.toString()+"z4A4"};
kb[i*28+16]={ke:3,time:"n"+i.toString()+"z5A1"};
kb[i*28+17]={ke:4,time:"n"+i.toString()+"z5A2"};
kb[i*28+18]={ke:5,time:"n"+i.toString()+"z5A3"};
kb[i*28+19]={ke:0,time:"n"+i.toString()+"z5A4"};
kb[i*28+20]={ke:0,time:"n"+i.toString()+"z6A1"};
kb[i*28+21]={ke:0,time:"n"+i.toString()+"z6A2"};
kb[i*28+22]={ke:0,time:"n"+i.toString()+"z6A3"};
kb[i*28+23]={ke:0,time:"n"+i.toString()+"z6A4"};
kb[i*28+24]={ke:0,time:"n"+i.toString()+"z7A1"};
kb[i*28+25]={ke:0,time:"n"+i.toString()+"z7A2"};
kb[i*28+26]={ke:0,time:"n"+i.toString()+"z7A3"};
kb[i*28+27]={ke:0,time:"n"+i.toString()+"z7A4"};
}
for (let i = 15; i < 20; i++) {
kb[i*28+0]={ke:1,time:"n"+i.toString()+"z1A1"};
kb[i*28+1]={ke:0,time:"n"+i.toString()+"z1A2"};
kb[i*28+2]={ke:0,time:"n"+i.toString()+"z1A3"};
kb[i*28+3]={ke:0,time:"n"+i.toString()+"z1A4"};
kb[i*28+4]={ke:0,time:"n"+i.toString()+"z2A1"};
kb[i*28+5]={ke:0,time:"n"+i.toString()+"z2A2"};
kb[i*28+6]={ke:3,time:"n"+i.toString()+"z2A3"};
kb[i*28+7]={ke:0,time:"n"+i.toString()+"z2A4"};
kb[i*28+8]={ke:1,time:"n"+i.toString()+"z3A1"};
kb[i*28+9]={ke:0,time:"n"+i.toString()+"z3A2"};
kb[i*28+10]={ke:0,time:"n"+i.toString()+"z3A3"};
kb[i*28+11]={ke:0,time:"n"+i.toString()+"z3A4"};
kb[i*28+12]={ke:1,time:"n"+i.toString()+"z4A1"};
kb[i*28+13]={ke:0,time:"n"+i.toString()+"z4A2"};
kb[i*28+14]={ke:3,time:"n"+i.toString()+"z4A3"};
kb[i*28+15]={ke:0,time:"n"+i.toString()+"z4A4"};
kb[i*28+16]={ke:3,time:"n"+i.toString()+"z5A1"};
kb[i*28+17]={ke:0,time:"n"+i.toString()+"z5A2"};
kb[i*28+18]={ke:5,time:"n"+i.toString()+"z5A3"};
kb[i*28+19]={ke:0,time:"n"+i.toString()+"z5A4"};
kb[i*28+20]={ke:0,time:"n"+i.toString()+"z6A1"};
kb[i*28+21]={ke:0,time:"n"+i.toString()+"z6A2"};
kb[i*28+22]={ke:0,time:"n"+i.toString()+"z6A3"};
kb[i*28+23]={ke:0,time:"n"+i.toString()+"z6A4"};
kb[i*28+24]={ke:0,time:"n"+i.toString()+"z7A1"};
kb[i*28+25]={ke:0,time:"n"+i.toString()+"z7A2"};
kb[i*28+26]={ke:0,time:"n"+i.toString()+"z7A3"};
kb[i*28+27]={ke:0,time:"n"+i.toString()+"z7A4"};
//kb.push(JSON.stringify(kb));
}
const compressedText = JSON.stringify(kb);
fs.appendFile('wj3.json',compressedText, (err) => {
if (err) throw err;
console.log('数据已成功写入文件');
});

@ -0,0 +1,64 @@
const fs=require('fs');
const moment=require('moment');
var mysql = require('mysql');
const LZString = require('lz-string');
const e = require("express");
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '416416',
database : 'neigongda'
});
function decompressText(compressedText) {
return LZString.decompressFromEncodedURIComponent(compressedText);
}
connection.connect();
function qjkebiao(xuehao,start,end) {
const startxueiq=moment("2024-09-02T00:00:27+08:00");
const daystart=moment(start).diff(startxueiq,"days");
const dayend=moment(end).diff(startxueiq,"days");
console.log(daystart);
const minutestart=moment(start).minutes()+moment(start).hours()*60;
const minutesend=moment(end).minutes()+moment(end).hours()*60;
let kestart=0;
if(minutestart<500){
kestart=1;
}else if(minutestart<620){
kestart=2;
}else if(minutestart<840){
kestart=3;
}else if(minutestart<960){
kestart=4;
}else {
kestart=5;
}
let keend=0;
if(minutesend>1060){
keend=4;
}else if(minutesend<940){
keend=3;
}else if(minutesend<720){
keend=2;
}else if(minutesend<600){
keend=1;
}else {
keend=0;
}
const kaishi=daystart+kestart*4;
const jieshu=dayend+keend*4;
const sql = 'SELECT xunhuan(?,?,?)';
connection.query(sql, [kaishi,jieshu,xuehao], (error, results, fields) => {
if (error) throw error;
// 处理查询结果
console.log(results);
});
console.log(dayend);
return 0;
}
exports.qjkebiao = qjkebiao;

@ -0,0 +1,123 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="robots" content="NONE,NOARCHIVE">
<title>403 Forbidden</title>
<style>
html * {
padding: 0;
margin: 0;
}
body * {
padding: 10px 20px;
}
body * * {
padding: 0;
}
body {
font-family: sans-serif;
background: #eee;
color: #000;
}
body>div {
border-bottom: 1px solid #ddd;
}
h1 {
font-weight: normal;
margin-bottom: .4em;
}
h1 span {
font-size: 60%;
color: #666;
font-weight: normal;
}
#info {
background: #f6f6f6;
}
#info ul {
margin: 0.5em 4em;
}
#info p,
#summary p {
padding-top: 10px;
}
#summary {
background: #ffc;
}
#explanation {
background: #eee;
border-bottom: 0px none;
}
</style>
</head>
<body>
<div id="summary">
<h1>Forbidden <span>(403)</span></h1>
<p>CSRF verification failed. Request aborted.</p>
<p>You are seeing this message because this site requires a CSRF cookie when submitting forms. This cookie is
required for security reasons, to ensure that your browser is not being hijacked by third parties.</p>
<p>If you have configured your browser to disable cookies, please re-enable them, at least for this site, or for
“same-origin” requests.</p>
</div>
<div id="info">
<h2>Help</h2>
<p>Reason given for failure:</p>
<pre>
CSRF cookie not set.
</pre>
<p>In general, this can occur when there is a genuine Cross Site Request Forgery, or when
<a href="https://docs.djangoproject.com/en/5.1/ref/csrf/">Djangos
CSRF mechanism</a> has not been used correctly. For POST forms, you need to
ensure:</p>
<ul>
<li>Your browser is accepting cookies.</li>
<li>The view function passes a <code>request</code> to the templates <a
href="https://docs.djangoproject.com/en/5.1/topics/templates/#django.template.backends.base.Template.render"><code>render</code></a>
method.</li>
<li>In the template, there is a <code>{% csrf_token
%}</code> template tag inside each POST form that
targets an internal URL.</li>
<li>If you are not using <code>CsrfViewMiddleware</code>, then you must use
<code>csrf_protect</code> on any views that use the <code>csrf_token</code>
template tag, as well as those that accept the POST data.</li>
<li>The form has a valid CSRF token. After logging in in another browser
tab or hitting the back button after a login, you may need to reload the
page with the form, because the token is rotated after a login.</li>
</ul>
<p>Youre seeing the help section of this page because you have <code>DEBUG =
True</code> in your Django settings file. Change that to <code>False</code>,
and only the initial error message will be displayed. </p>
<p>You can customize this page using the CSRF_FAILURE_VIEW setting.</p>
</div>
</body>
</html>

@ -0,0 +1 @@
[{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":6,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":6,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":6,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":2,"z1A3":0,"z1A4":6,"z2A1":0,"z2A2":2,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":4,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":2,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":4,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":0,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":0,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":0,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":0,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":0,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":0,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":0,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":0,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":0,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":0,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":0,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":0,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":0,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":0,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":0,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":0,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":0,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":0,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":0,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":0,"z5A3":5,"z5A4":0},{"z1A1":1,"z1A2":0,"z1A3":0,"z1A4":0,"z2A1":0,"z2A2":0,"z2A3":3,"z2A4":0,"z3A1":1,"z3A2":0,"z3A3":0,"z3A4":0,"z4A1":1,"z4A2":0,"z4A3":3,"z4A4":0,"z5A1":3,"z5A2":0,"z5A3":5,"z5A4":0}]

@ -0,0 +1 @@
N4IgJgDARgjAgjEAuGAac14CZlfZWOAZmQn0zgBZTyosEaM64ck8n6Skjb7qkyTIgxS1hrSmOKMCw-oIKURaJktbtF07rSXzaAVhE8mhiQa37ze8DEKJRNwuvy34XBa6ozb9ex+bOjpzIxmA+XgIuUML2KmHRLMiSjsLuUXLeUEqxUWq4uVqhtrqZhvZFUKZJUYZclo6G1mBYdshxLdj54B1aCj1NLb4yg4ls+CNcoSMDCTndCWbzqcMJM9lt41mjGi1Kk5slkfNlIZtVSMnNlRZnEQpErQ5gD51j4C+9+B9ND0NHzwEugDgtp3swfrMNmDxNVoZ84RD1k8Hnk3gC9qcwYd7tdyl9rosAbVkPUiXd8JRHnFKa8NDT4WB6U1KX8FCzthTmPtwOzmZCnpSYRdOct-oLyTytnNGVtAjKMaD5RKZSdFZTzpd1TdJY0ZPoqfh9bTDYQ0uAjcr9ayTfQ5VbCjbLfy4vqhZdXQyPU19UiXbKgT6Heatt7cZiwPqNSbiUhSZHlQA2A3gJPGlOmmSpxPMPz4JO2oH5oNgItNJMxKElhawquihTl7O+vP+tFJhWhNvZ1Udgk1pMx0n95UAdmTYFHafHGf+E5HOZko4LaMXxZXTVHFaeG9Gl23ZqnGRnUsro9RGlPq5DC7DitHUfAd+1U91-wAHGP35PPwzv0139bwH-DlAK5cMgL-Z18HfN0oISfdoOVd8mxAs9YPbNDEJvUJ33vMAcKffCmgATjHEjJzIhkKOI+d-hIpcNDo4tGOozc4hImDwHYyjVhkEjkLAPjgIErZuWE7F8BI7sJN7YVOOuOppJfDwKGlGAKDlNTp2Uwgmk0gCwmgeiXEM4s9OVTTWOM6tZIMuDvGgQ9tP4zTUJsaB0Lcq9-k0qTPNwnynwC3TPFUzwNM8fcYE8fgExccJcxscJwtAxUovBbxbEsxLrMuNK6zinjvOKZQCtcsJitEtLxOy3zypk3LbAHAqlJcHpVJ6DSeki-pkFimwRgSsIRk6lKimmbwWiyoactauzvMm8zdhK-qWw0GAlsqpbdJaWr1vq2ampWlqbA+VSPg0j5Iu+XqXF+EQPDui7Rtu9LvIeKaYHencXvyk7Coe48nk+1aXo8sIUXMh5dqh76-sO8Hrl0+lVPpDT6UipkbpsdlBpgdk0ee7HXo8cUUZmonfrCcUkcBuI8ZBomwfp6qqawlwtUJen4a58yLVUi1WA8C1IotXT7Vx+1BZce1KvtMXIJsD0pcVuahf+6XaY11E1aZwN5d2+NlbCeMFJV46wizVSsyNmAs0irNdKLXGixtotKtLbwGyt6yPAbe31ZsTtve1lxO3drzfbZwOZMj7mh102dVNnG3Z0i2cE5ojwVxTwmwjXbxtyTn2XD3AuA7zzWbAvHOmYvDPdsfHPucfXTv1U78be-SLf28IDcaAzvc5gcDe4VsIEMHynh-L6fnKQ0YPHnyr59bqPx5jlx8NN9fzZgCjVIom2KMiqjvEY3HGKPofmLPse9+LmwuJPmfBIPlsPEEyrBN0ySjBcX+r7c1-vIAAvkAA

File diff suppressed because one or more lines are too long

10
shims-uni.d.ts vendored

@ -0,0 +1,10 @@
/// <reference types='@dcloudio/types' />
import 'vue'
declare module '@vue/runtime-core' {
type Hooks = App.AppInstance & Page.PageInstance;
interface ComponentCustomOptions extends Hooks {
}
}

@ -0,0 +1,14 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide } from "@dcloudio/uni-app";
onLaunch(() => {
console.log("App Launch");
});
onShow(() => {
console.log("App Show");
});
onHide(() => {
console.log("App Hide");
});
</script>
<style></style>

@ -0,0 +1,87 @@
<template>
<view
class="tech-button"
:class="[type, size]"
@click="$emit('click')"
>
<slot></slot>
</view>
</template>
<script setup>
defineProps({
type: {
type: String,
default: 'primary', // primary | secondary
validator: (value) => ['primary', 'secondary'].includes(value)
},
size: {
type: String,
default: 'normal', // normal | small
validator: (value) => ['normal', 'small'].includes(value)
}
})
</script>
<style lang="scss" scoped>
.tech-button {
display: inline-flex;
justify-content: center;
align-items: center;
gap: 10rpx;
border-radius: 50rpx;
font-weight: 500;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%);
opacity: 0;
transition: opacity 0.3s ease;
}
&.primary {
background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%);
color: #fff;
box-shadow: 0 4rpx 20rpx rgba(74, 144, 226, 0.3);
&:active {
transform: translateY(2rpx);
box-shadow: 0 2rpx 10rpx rgba(74, 144, 226, 0.3);
}
}
&.secondary {
background: rgba(16, 24, 40, 0.8);
color: #4facfe;
border: 1rpx solid rgba(74, 144, 226, 0.5);
&:active {
background: rgba(31, 47, 71, 0.8);
}
}
&.normal {
height: 80rpx;
padding: 0 40rpx;
font-size: 30rpx;
}
&.small {
height: 60rpx;
padding: 0 30rpx;
font-size: 26rpx;
}
&:active::before {
opacity: 0.2;
}
}
</style>

@ -0,0 +1,89 @@
<template>
<view
class="tech-card"
:class="{ active }"
@click="$emit('click')"
>
<view class="card-bg"></view>
<view class="card-content">
<uni-icons
:type="icon"
size="28"
:color="active ? '#4facfe' : 'rgba(255,255,255,0.7)'"
/>
<text class="card-title">{{ title }}</text>
</view>
<view class="card-decoration"></view>
</view>
</template>
<script setup>
defineProps({
title: String,
icon: String,
active: Boolean
})
</script>
<style lang="scss" scoped>
.tech-card {
height: 140rpx;
border-radius: 16rpx;
position: relative;
overflow: hidden;
transition: all 0.3s ease;
.card-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(16, 24, 40, 0.8);
border: 1px solid rgba(74, 144, 226, 0.2);
backdrop-filter: blur(5px);
z-index: 1;
}
.card-content {
position: relative;
z-index: 2;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20rpx;
.card-title {
font-size: 28rpx;
margin-top: 15rpx;
color: rgba(255, 255, 255, 0.9);
}
}
.card-decoration {
position: absolute;
bottom: 0;
right: 0;
width: 40rpx;
height: 40rpx;
background: radial-gradient(circle, rgba(74, 144, 226, 0.3) 0%, transparent 70%);
z-index: 1;
}
&.active {
transform: translateY(-5rpx);
box-shadow: 0 10rpx 20rpx rgba(0, 242, 254, 0.2);
.card-bg {
background: rgba(31, 47, 71, 0.8);
border-color: rgba(74, 144, 226, 0.5);
}
.card-decoration {
background: radial-gradient(circle, rgba(74, 144, 226, 0.6) 0%, transparent 70%);
}
}
}
</style>

@ -0,0 +1,59 @@
<template>
<view class="tech-progress">
<view
class="progress-bar"
:style="{ width: `${percent}%` }"
>
<view class="progress-highlight"></view>
</view>
<text class="progress-text">{{ percent }}%</text>
</view>
</template>
<script setup>
defineProps({
percent: {
type: Number,
default: 0,
validator: (value) => value >= 0 && value <= 100
}
})
</script>
<style lang="scss" scoped>
.tech-progress {
height: 16rpx;
background: rgba(16, 24, 40, 0.8);
border-radius: 8rpx;
position: relative;
overflow: hidden;
border: 1rpx solid rgba(74, 144, 226, 0.3);
.progress-bar {
height: 100%;
border-radius: 8rpx;
background: linear-gradient(90deg, #00f2fe 0%, #4facfe 100%);
position: relative;
transition: width 0.6s ease;
.progress-highlight {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, rgba(255,255,255,0.3) 0%, transparent 100%);
border-radius: 8rpx;
}
}
.progress-text {
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
font-size: 22rpx;
color: rgba(255, 255, 255, 0.8);
}
}
</style>

8
src/env.d.ts vendored

@ -0,0 +1,8 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}

@ -0,0 +1,8 @@
import { createSSRApp } from "vue";
import App from "./App.vue";
export function createApp() {
const app = createSSRApp(App);
return {
app,
};
}

@ -0,0 +1,77 @@
{
"name" : "",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false,
"minified" : true
},
"usingComponents" : true,
"optimization" : {
"subPackages" : true
},
"lazyCodeLoading": "requiredComponents"
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3"
}

@ -0,0 +1,185 @@
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true
}
},
{
"path": "pages/learning/learning",
"style": {
"navigationBarTitleText": "课程学习"
}
},
{
"path": "pages/management/management",
"style": {
"navigationBarTitleText": "课程管理"
}
},
{
"path": "pages/schedule/schedule",
"style": {
"navigationBarTitleText": "智能课程表",
"navigationBarBackgroundColor": "#0a192f",
"navigationBarTextStyle": "white",
"backgroundColor": "#0a192f"
}
},
{
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "个人中心"
}
},
{
"path": "pages/leave/leave",
"style": {
"navigationBarTitleText": "leave"
}
},
{
"path": "pages/plan/plan",
"style": {
"navigationBarTitleText": "plan"
}
},
{
"path": "pages/bussin/bussin",
"style": {
"navigationBarTitleText": "bussin"
}
},
{
"path": "pages/course/course",
"style": {
"navigationBarTitleText": "course"
}
},
{
"path": "pages/score/score",
"style": {
"navigationBarTitleText": "score"
}
},
{
"path": "pages/settings/settings",
"style": {
"navigationBarTitleText": "settings"
}
},
{
"path": "pages/management/leave",
"style": {
"navigationBarTitleText": "leave"
}
},
{
"path": "pages/management/program",
"style": {
"navigationBarTitleText": "program"
}
},
{
"path": "pages/management/transcript",
"style": {
"navigationBarTitleText": "transcript"
}
},
{
"path": "pages/management/selection",
"style": {
"navigationBarTitleText": "selection"
}
},
{
"path": "pages/list/list",
"style": {
"navigationBarTitleText": "list"
}
},
{
"path": "pages/discussion/discussion",
"style": {
"navigationBarTitleText": "discussion"
}
},
{
"path": "pages/checkin/checkin",
"style": {
"navigationBarTitleText": "checkin"
}
},
{
"path": "pages/quiz/quiz",
"style": {
"navigationBarTitleText": "quiz"
}
},{
"path": "pages/quiz_outclass/quiz_outclass",
"style": {
"navigationBarTitleText": "课程答题"
}
},{
"path": "pages/quiz_outclass/selectquestion",
"style": {
"navigationBarTitleText": "课程答题"
}
}
],
"subPackages": [{
"root": "pages_A",
"pages": [{
"path": "exam/selectexam",
"style": {
"navigationBarTitleText": "课程考试"
}
},{
"path": "exam/exam",
"style": {
"navigationBarTitleText": "exam2"
}
},
{
"path": "shangke/shangke",
"style": {
"navigationBarTitleText": "shnagke"
}
}
]
}],
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#007AFF",
"list": [
{
"pagePath": "pages/learning/learning",
"text": "课程学习",
"iconPath": "static/edu-tabs/learning.png",
"selectedIconPath": "static/edu-tabs/learning-active.png"
},
{
"pagePath": "pages/schedule/schedule",
"text": "课程表",
"iconPath": "/static/edu-tabs/calendar.png",
"selectedIconPath": "/static/edu-tabs/calendar-active.png"
},
{
"pagePath": "pages/management/management",
"text": "课程管理",
"iconPath": "/static/edu-tabs/dashboard.png",
"selectedIconPath": "/static/edu-tabs/dashboard-active.png"
},
{
"pagePath": "pages/user/user",
"text": "我的",
"iconPath": "/static/edu-tabs/user.png",
"selectedIconPath": "/static/edu-tabs/user-active.png"
}
]
}
}

@ -0,0 +1,276 @@
<template>
<view class="container">
<view class="main" v-if="!showRankList">
<!-- 按钮区域 -->
<view class="button-container">
<button
v-if="activityStatus === '活动中'"
class="round-button"
@click="handleClick"
>
</button>
<view v-else class="status-text">
{{ activityStatus }}
</view>
</view>
<!-- 排行榜区域 -->
</view>
<view v-else >
<view class="title-view">
<text class="title">抢答</text>
</view>
<br/>
<text class="paimin">我的排名: {{pm}}</text>
<transition name="fade-slide">
<view class="rank-list">
<view
v-for="(item, index) in rankList"
:key="index"
class="rank-item"
:class="{
'black':1===1
}"
>
<text class="rank-number">{{ index + 1 }}</text>
<text class="rank-name">{{JSON.parse(item.infor).name }}</text>
<text class="rank-time">{{ (JSON.parse(item.infor).time/1000).toFixed() }}s</text>
</view>
</view>
</transition>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import {onLoad} from "@dcloudio/uni-app";
import moment from "moment";
const uid=ref('1');
const starttime=ref('123')
const rankList = ref([
]);
const pm=ref(0)
onLoad((options) => {
console.log('load', options)
if(options) {
console.log('options:', options.uuid)
uni.request({
url: 'http://localhost:3400/apistu/getinclassactive',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: options.uuid
},
success: (res) => { /* ... */
console.log(res.data)
let infor=JSON.parse(res.data.infor)
console.log(infor.endtime)
starttime.value=infor.time;
uid.value=res.data.uuid;
}
});
uni.request({
url: 'http://localhost:3400/apistu/getqiangdainclass',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: options.uuid,
},
success: (res) => { /* ... */
console.log(res.data)
rankList.value=res.data
let index=res.data.findIndex(item=>item.xuehao==='202413501062');
if(index!=-1){
showRankList.value = true; //
activityStatus.value = '未在活动中'; //
pm.value=index+1
}
if(res.data.length>0) {
}
}
});
}
})
//
const activityStatus = ref('活动中'); // '', ''
const showRankList = ref(false); //
//
//
const handleClick = () => {
console.log('抢答按钮被点击');
uni.request({
url: 'http://localhost:3400/apistu/addinactivestu_qiangda',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: uid.value,
kch:'202413501',
kctime:'n8z1A1',
xuehao: '202413501063',
time:moment().format('YYYY-MM-DD HH:mm:ss'),
starttime:starttime.value,
name:"刘东阳A",
touxiangcolor:"red"
},
success: (res) => { /* ... */
console.log(res.data)
rankList.value.push(res.data)
showRankList.value = true; //
uni.$emit('finish_ex',{uuid:uid.value,wb:'已完成'}
);
activityStatus.value = '未在活动中'; //
}
});
};
</script>
<style scoped>
.container {
display: flex;
flex-direction: column;
height: 100vh;
background: linear-gradient(to bottom, #f8f9fa, #e9ecef);
}
.main {
display: flex;
justify-content: center;
align-items: center;
flex: 1;
padding: 20px;
}
/* 按钮样式保持不变 */
.round-button {
width: 200px;
height: 200px;
border-radius: 50%;
background-color: #4a90e2;
color: #fff;
font-size: 48px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
.round-button:hover {
transform: scale(1.05);
}
.status-text {
font-size: 48px;
color: #999;
}
/* 排行榜容器 */
.rank-list-container {
padding: 0;
width: 100%;
max-width: 500px;
margin: 0 auto;
}
.rank-list {
border-radius: 16px;
padding: 20px;
margin-top: 4px;
}
.rank-title {
display: block;
font-size: 28px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
color: #333;
}
.rank-item {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #999;
padding: 12px 16px;
border-radius: 12px;
margin-bottom: 12px;
transition: background-color 0.3s ease;
}
.rank-item.gold {
background-color: #ffd700;
}
.rank-item.silver {
background-color: #c0c0c0;
}
.rank-item.bronze {
background-color: #cd7f32;
color: #fff;
}
.rank-item.black {
background-color: #fff;
color: black;
}
.rank-number {
font-size: 20px;
font-weight: bold;
min-width: 30px;
}
.rank-name {
flex: 1;
font-size: 18px;
}
.rank-time {
font-size: 16px;
color: #555;
}
/* 动画效果 */
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: all 0.5s ease;
}
.fade-slide-enter-from,
.fade-slide-leave-to {
opacity: 0;
transform: translateY(20px);
}
.paimin{
position: relative;
top:9px;
margin-top: 10px;
color: #555;
margin-left: 20px;
}
.title{
margin: 20px;
position: relative;
top:9px
}
.title-view{
height: 40px;
position: relative;
justify-content: center;
background-color: white;
}
</style>

@ -0,0 +1,438 @@
<template>
<view class="checkin-container">
<!-- 累计时间显示 -->
<view>
<span>{{qdttitle}}</span>
</view>
<view v-if="finish==='no'">
<div class="time-display">
<span>累计时间</span>
<span>{{ formattedTime }}</span>
</div>
<div v-if="selecttype === '普通签到'">
<!-- 立即签到按钮 -->
<button
v-if="totalTime > 0 "
@click="handleCheckIn"
class="checkin-button"
>
立即签到
</button>
</div>
<div v-if="selecttype === '伪签到'">
<!-- 立即签到按钮 -->
<button
v-if="totalTime > 0 "
@click="handleCheckIn"
class="checkin-button"
>
立即签到
</button>
</div>
<div v-if="selecttype === '密码签到'">
<div class="password-checkin">
<p>请输入签到密码</p>
<input type="password" v-model="password" placeholder="密码" />
<button @click="handlePasswordCheckIn"></button>
<div v-if="passwordCheckInResult" :class="['checkin-result', passwordCheckInResult === '签到成功' ? 'success' : 'error']">
{{ passwordCheckInResult }}
</div>
</div>
</div>
<div v-if="selecttype === '二维码签到'">
<div class="scan-checkin">
<p>请扫描二维码进行签到</p>
<button @click="onDecode" class="scan-button">开始扫码</button>
</div>
</div>
</view>
<!-- 不可签到状态 -->
<div v-if="totalTime <= 0 && checkInResult !== '签到成功'" class="checkin-result">
不可签到
</div>
<view v-if="finish==='finish'">
<!-- 签到成功提示 -->
<div v-if="selecttype!=='伪签到'" class="checkin-result">
{{ checkInResult }}
</div>
<div v-if="selecttype==='伪签到'" class="checkin-result">
请诚信学习
</div>
<div v-if="checkInResult === '签到成功'" class="checkin-result-time">
{{qidndaotime}}
</div>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, computed } from 'vue';
import {onLoad} from "@dcloudio/uni-app";
import moment from "moment";
//
const selecttype = ref('扫描签到');
const initialTime = 10; // 60
const totalTime = ref(initialTime);
const intervalId = ref<number | null>(null); //
const uid=ref('123')
const qdttitle=ref('')
//
const formattedTime = computed(() => {
const minutes = Math.floor(totalTime.value / 60);
const seconds = totalTime.value % 60;
return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
});
const password = ref(''); //
const passwordCheckInResult = ref(''); //
const endtime=ref('')
const finish=ref('no')
const qidndaotime=ref('')
const checkInResult = ref('');
const handlePasswordCheckIn = () => {
//
const correctPassword = '123456'; //
if (totalTime.value > 0) {
uni.request({
url: 'http://localhost:3400/apistu/qdstuinclass_password',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: uid.value,
kch:'202413501',
kctime:'n8z1A1',
xuehao: '202413501062',
output:'no',
password:password.value,
time:moment().format('YYYY-MM-DD HH:mm:ss')
},
success: (res) => { /* ... */
if(res.data.stats==="ok") {
console.log(res.data);
passwordCheckInResult.value = '签到成功';
stopTimer(); //
}else{
uni.showModal({
title: '提示',
content: '签到密码错误',
});
}
}
});
}
else {
passwordCheckInResult.value = '签到时间已结束';
}
};
onLoad((options) => {
console.log('load', options)
if(options) {
console.log('options:', options.uuid)
uni.request({
url: 'http://localhost:3400/apistu/getinclassactivestu_ex',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: options.uuid,
xuehao:'202413501062'
},
success: (res) => { /* ... */
console.log(res.data)
if(res.data.length>0) {
qidndaotime.value = res.data[0].time;
checkInResult.value = '签到成功';
finish.value = 'finish'
}
}
});
uid.value=options.uuid
uni.request({
url: 'http://localhost:3400/apistu/getinclassactive',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: options.uuid
},
success: (res) => { /* ... */
console.log(res.data)
let infor=JSON.parse(res.data.infor)
console.log(infor.endtime)
endtime.value = infor.endtime
qdttitle.value=infor.title
selecttype.value=infor.lxA
}
});
}
})
//
const startTimer = () => {
intervalId.value = setInterval(() => {
if (totalTime.value > 0) {
totalTime.value=moment(endtime.value).diff(moment(),'seconds')
} else {
stopTimer();
}
}, 1000);
};
//
const stopTimer = () => {
if (intervalId.value !== null) {
clearInterval(intervalId.value);
intervalId.value = null;
}
};
//
const handleCheckIn = () => {
if (totalTime.value > 0) {
//
checkInResult.value = '签到成功';
stopTimer(); //
uni.request({
url: 'http://localhost:3400/apistu/qdstuinclass',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: uid.value,
kch:'202413501',
kctime:'n8z1A1',
xuehao: '202413501062',
output:'no',
time:moment().format('YYYY-MM-DD HH:mm:ss')
},
success: (res) => { /* ... */
finish.value='finish'
checkInResult.value = '签到成功';
}
});
} else {
checkInResult.value = '已过签到时间';
}
};
//
const onDecode = () => {
uni.scanCode({
success: (res) => {
console.log('扫码结果:', res.result);
if (totalTime.value > 0) {
console.log(1245)
if(JSON.parse(res.result).qduuid){
uni.request({
url: 'http://localhost:3400/apistu/qdstuinclass_qrcode1',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: uid.value,
kch:'202413501',
kctime:'n8z1A1',
xuehao: '202413501062',
output:'no',
qduuid:JSON.parse(res.result).qduuid,
time:moment().format('YYYY-MM-DD HH:mm:ss')
},
success: (res_2) => { /* ... */
if(res_2.data.stats==="ok") {
console.log(res.data);
passwordCheckInResult.value = '签到成功';
uni.$emit('finish', uid.value)
stopTimer(); //
}else{
uni.showModal({
title: '提示',
content: '签到密码错误',
});
}
}
});
}
checkInResult.value = '签到成功';
stopTimer();
} else {
checkInResult.value = '已过签到时间';
}
},
fail: (err) => {
console.error('扫码失败:', err);
}
});
};
//
onMounted(() => {
startTimer();
});
//
onUnmounted(() => {
stopTimer();
});
</script>
<style scoped>
.checkin-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh; /* 使容器高度为视口高度 */
padding: 20px;
background-color: #f9f9f9;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
.time-display {
text-align: center;
margin-bottom: 20px;
font-size: 14px; /* 缩小“累计时间”文本的字体大小 */
color: #333;
}
.time-display span:first-child {
display: block;
font-size: 14px;
color: #666;
margin-bottom: 5px;
}
.time-display span:last-child {
font-size: 36px;
color: #333;
}
.checkin-button {
background-color: #ff4d4f;
color: white;
border: none;
border-radius: 50%;
width: 150px; /* 进一步增大按钮尺寸 */
height: 150px;
font-size: 24px; /* 增大按钮内文字的字体大小 */
transition: background-color 0.3s;
display: flex;
align-items: center;
justify-content: center;
margin: 20px 0; /* 调整按钮与上下元素的间距 */
}
.checkin-button:hover {
background-color: #e53935;
}
.checkin-result {
margin-top: 20px;
font-size: 18px;
color: green;
font-weight: bold;
}
.password-checkin {
text-align: center;
margin-bottom: 20px;
}
.password-checkin p {
font-size: 18px;
color: #333;
margin-bottom: 10px;
}
.password-checkin input {
padding: 10px;
margin-bottom: 10px;
border: 1px solid #ccc;
border-radius: 4px;
width: 200px;
text-align: center;
}
.password-checkin button {
background-color: #ff4d4f;
color: white;
border: none;
border-radius: 50px;
width: 150px;
height: 40px;
font-size: 16px;
transition: background-color 0.3s;
}
.password-checkin button:hover {
background-color: #e53935;
}
.checkin-result {
margin-top: 20px;
font-size: 18px;
margin-left: 10px;
font-weight: bold;
}
.checkin-result.success {
color: green;
}
.checkin-result-time {
margin-top: 20px;
font-size: 12px;
font-weight: bold;
color: #7f8fa6;
}
.checkin-result-time.success {
color: green;
}
.checkin-result.error {
color: red;
}
.scan-checkin {
text-align: center;
margin-bottom: 20px;
}
.scan-checkin p {
font-size: 18px;
color: #333;
margin-bottom: 10px;
}
.scan-button {
background-color: #4CAF50; /* 绿色背景 */
color: white;
border: none;
border-radius: 50px; /* 圆角按钮 */
padding: 12px 24px;
font-size: 16px;
font-weight: bold;
width: 80%;
max-width: 300px;
margin: 10px auto;
display: block;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影增加质感 */
transition: background-color 0.3s ease, transform 0.2s ease;
}
.scan-button:hover {
background-color: #45a049;
transform: translateY(-2px); /* 微动效果 */
}
.scan-button:active {
transform: scale(0.98); /* 按下缩小一点 */
}
</style>

@ -0,0 +1,284 @@
<template>
<view class="tech-container">
<!-- 科技风顶部导航 -->
<view class="tech-header">
<view class="header-content">
<text class="header-title">我的课程中心</text>
<view class="header-line"></view>
</view>
<view class="tech-pattern"></view>
</view>
<!-- 课程内容区域 -->
<view class="course-content">
<!-- 课程分类导航 -->
<scroll-view class="category-nav" scroll-x>
<view
v-for="(category, index) in categories"
:key="index"
class="category-item"
:class="{ active: activeCategory === index }"
@click="switchCategory(index)"
>
<text>{{ category }}</text>
</view>
</scroll-view>
<!-- 课程列表 -->
<view class="course-list">
<view
v-for="(course, index) in filteredCourses"
:key="index"
class="course-card"
>
<image class="course-cover" :src="course.cover" mode="aspectFill" />
<view class="course-info">
<view class="title-wrapper">
<text class="course-title">{{ course.title }}</text>
<text class="course-teacher">{{ course.teacher }}</text>
</view>
<view class="progress-bar">
<view
class="progress-fill"
:style="{ width: `${course.progress}%` }"
></view>
</view>
<text class="progress-text">已学 {{ course.progress }}%</text>
</view>
<view class="tech-corner"></view>
</view>
</view>
</view>
<!-- 科技风底部装饰 -->
<view class="tech-footer">
<view class="footer-line"></view>
<view class="footer-dots">
<view class="dot"></view>
<view class="dot"></view>
<view class="dot"></view>
</view>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
//
const categories = ref(['全部', '进行中', '已完成', '未开始', '收藏'])
const activeCategory = ref(0)
//
const courses = ref([
{
id: 1,
title: 'Vue3高级开发实战',
teacher: '张老师',
cover: '/static/student-course/bg.png',
progress: 65,
category: '进行中'
},
{
id: 2,
title: 'React全栈开发',
teacher: '李老师',
cover: '/static/student-course/bg.png',
progress: 30,
category: '进行中'
},
{
id: 3,
title: 'Node.js后端开发',
teacher: '王老师',
cover: '/static/course3.jpg',
progress: 100,
category: '已完成'
}
])
//
const switchCategory = (index) => {
activeCategory.value = index
}
//
const filteredCourses = computed(() => {
if (activeCategory.value === 0) return courses.value
const categoryMap = ['全部', '进行中', '已完成', '未开始', '收藏']
return courses.value.filter(course =>
course.category === categoryMap[activeCategory.value]
)
})
</script>
<style lang="scss">
.tech-container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #0f1621;
color: #e0e0e0;
}
.tech-header {
position: relative;
padding: 40rpx 30rpx 30rpx;
background: linear-gradient(135deg, #1a2a3a 0%, #0f1621 100%);
overflow: hidden;
z-index: 1;
.header-content {
position: relative;
z-index: 2;
}
.header-title {
font-size: 42rpx;
font-weight: bold;
background: linear-gradient(to right, #4facfe, #00f2fe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.header-line {
height: 4rpx;
width: 80rpx;
background: linear-gradient(to right, #4facfe, #00f2fe);
margin-top: 15rpx;
border-radius: 2rpx;
}
.tech-pattern {
position: absolute;
top: 0;
right: 0;
width: 300rpx;
height: 100%;
background: linear-gradient(135deg, rgba(79, 172, 254, 0.1) 0%, transparent 70%);
opacity: 0.3;
}
}
.course-content {
flex: 1;
padding: 20rpx;
}
.category-nav {
white-space: nowrap;
margin-bottom: 30rpx;
.category-item {
display: inline-block;
padding: 15rpx 30rpx;
margin-right: 20rpx;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.7);
border-radius: 40rpx;
transition: all 0.3s;
&.active {
background: linear-gradient(to right, #4facfe, #00f2fe);
color: #fff;
box-shadow: 0 5rpx 15rpx rgba(79, 172, 254, 0.3);
}
}
}
.course-list {
display: flex;
flex-direction: column;
gap: 30rpx;
}
.course-card {
position: relative;
background-color: rgba(255, 255, 255, 0.05);
border-radius: 16rpx;
overflow: hidden;
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.2);
.course-cover {
width: 100%;
height: 300rpx;
}
.course-info {
padding: 25rpx;
}
.title-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.course-title {
font-size: 32rpx;
font-weight: bold;
flex: 1;
}
.course-teacher {
font-size: 26rpx;
color: #4facfe;
margin-left: 20rpx;
}
.progress-bar {
height: 8rpx;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 4rpx;
margin-bottom: 10rpx;
overflow: hidden;
.progress-fill {
height: 100%;
background: linear-gradient(to right, #4facfe, #00f2fe);
border-radius: 4rpx;
}
}
.progress-text {
font-size: 24rpx;
color: rgba(79, 172, 254, 0.8);
}
.tech-corner {
position: absolute;
top: 0;
right: 0;
width: 0;
height: 0;
border-style: solid;
border-width: 0 60rpx 60rpx 0;
border-color: transparent rgba(79, 172, 254, 0.2) transparent transparent;
}
}
.tech-footer {
padding: 30rpx 0;
position: relative;
.footer-line {
height: 1rpx;
background: linear-gradient(to right, transparent, rgba(79, 172, 254, 0.3), transparent);
}
.footer-dots {
display: flex;
justify-content: center;
gap: 20rpx;
margin-top: 20rpx;
.dot {
width: 10rpx;
height: 10rpx;
border-radius: 50%;
background-color: rgba(79, 172, 254, 0.5);
}
}
}
</style>

@ -0,0 +1,17 @@
<script setup >
import { onMounted, onUnmounted } from 'vue'
import moment from "moment/min/moment-with-locales";
import io from '@hyoga/uni-socket.io'
import { ref } from 'vue'
import {onLoad} from "@dcloudio/uni-app";
</script>
<template>
</template>
<style scoped>
</style>

@ -0,0 +1,318 @@
<template>
<view class="discussion">
<!-- 教师区域 -->
<view v-if="content.length > 0">
<view v-for="item in content" :key="item.tid">
<!-- 用户信息 -->
<view class="user-info">
<image class="avatar" :src="item.avatar"></image>
<view class="user-details">
<text class="username">{{ item.username }}</text><br>
<text class="timestamp">{{ item.timestamp }} 回复:{{ reply }} 阅读:{{ reading }}</text>
</view>
</view>
<!-- 标题 -->
<view class="title">
<text>{{ item.title }}</text>
</view>
<!-- 内容 -->
<view class="content">
<view v-for="(item_xiang,index) in item.contentlist">
<view v-if="item_xiang.leixing==='video'">
<view>
<video :src="item_xiang.sp"></video>
</view>
</view>
<view v-if="item_xiang.leixing==='wb'">
<view>
<text>{{item_xiang.wb}}</text>
</view>
</view>
<view v-if="item_xiang.leixing==='pic'">
<view>
<image :src="item_xiang.pic" mode="aspectFit" />
</view>
</view>
</view>
</view>
</view>
</view>
<!-- 评论区 -->
<view class="comments">
<view v-for="comment in comments" :key="comment.id" class="comment">
<image class="avatar" :src="comment.avatar"></image>
<view class="comment-details">
<text class="username">{{ comment.author }}</text><br>
<text class="timestamp">{{ moment(comment.fabutime).format("MM-DD HH:mm") }}</text><br>
<text class="comment-content">{{ comment.neirong }}</text>
</view>
</view>
</view>
<!-- 发表评论 -->
<view class="comment-input">
<button class="add-comment-btn">+</button>
<input v-model="newCommentContent" placeholder="写下你的评论..."/>
<button @click="submitComment" class="submit-button">发表</button>
</view>
</view>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import {onLoad} from "@dcloudio/uni-app";
import uuidv4 from "../../utile/uuid/uuidv4";
import moment from "moment";
//
const content = ref([
{
tid: 0,
username: '白雪芹',
avatar: '/static/course-image/Avatar.png',
timestamp: '04-25 11:46',
title: '思考题',
content: '简述视频中的两起民事纠纷过程回答1、为什么案例1中的被告需承担民事责任、受到民事制裁2、为什么案例1中的被告不构成“高空抛物罪”3、案例2中的被告为什么无需担责请找到《中华人民共和国民法典》的具体条文规定分析讨论法院的判决是否合理。',
contentlist:[]
}
])
const comments = ref([])
const reply = computed(() => comments.value.length)
const reading = ref(1)
const uid=ref('12')
const finish=ref('no')
//
onLoad((options) => {
console.log('load', options)
if(options) {
console.log('options:', options.uuid)
uid.value=options.uuid
uni.request({
url: 'http://localhost:3400/api/selectcommit',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
xuhao: options.uuid
},
success: (res) => { /* ... */
console.log(res.data)
comments.value=res.data
}
});
uni.request({
url: 'http://localhost:3400/apistu/getinclassactive',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: options.uuid
},
success: (res) => { /* ... */
console.log(res.data)
let infor=JSON.parse(res.data.infor)
console.log(infor)
content.value[0].contentlist = infor.taolonglist
content.value[0].title=infor.title
content.value[0].username=res.data.teacher
content.value[0].timestamp=moment(res.data.time).format("MM-DD HH:mm")
}
});
}
})
const loadedComments = () => {
//
const saved = uni.getStorageSync('discussion_comments')
return saved || [
{
id: 1,
username: '王中铎',
avatar: '/static/course-image/Avatar.png',
timestamp: '04-25 11:54',
content: '1.根据《中华人民共和国民法典》第1254条规定禁止从建筑物中抛掷物和2.高空抛物罪的犯罪主体一般要求是年满十六周岁、具备刑事责任能力的自然人。如果小孩未满十六周岁,或者属于限制刑事责任能力人,可能无法构成..'
}
]
}
//
const newCommentContent = ref('')
//
const submitComment = async () => {
const text = newCommentContent.value.trim()
if (!text) return
uni.request({
url: 'http://localhost:3400/apistu/inclassfabutaolong',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: uid.value,
content: text,
xuehao: '202413501062'
},
success: (res) => { /* ... */
}
});
const newComment = {
id: Date.now(),
author: '刘东阳',
avatar: '/static/course-image/Avatar.png',
fabutime: formatTime(new Date()),
neirong: text
}
uni.request({
url: 'http://localhost:3400/api/fabucommit',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
dislikes: [],
author: "刘东阳",
uuid: "A1",
likes: [],
filelist: [],
neirong: text,
fabutime: moment().format(),
xuehao: "202413501062",
xuhao: uid.value//
},
success: (res) => { /* ... */
}
});
uni.$emit('finish', uid.value)
comments.value.unshift(newComment)
newCommentContent.value = ''
//
uni.setStorageSync('discussion_comments', comments.value)
}
//
const formatTime = (date: Date): string => {
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
const hours = date.getHours().toString().padStart(2, '0')
const minutes = date.getMinutes().toString().padStart(2, '0')
return `${month}-${day} ${hours}:${minutes}`
}
</script>
<style scoped>
.discussion {
padding: 20px;
}
.user-info {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.avatar {
width: 35px;
height: 35px;
border-radius: 50%;
margin-right: 10rpx;
}
.user-details .username {
font-weight: bold;
}
.user-details .timestamp {
color: #888;
font-size: 12px;
}
.title {
font-size: 21px;
margin: 20px 0;
}
.content {
margin-bottom: 20px;
padding-bottom: 30rpx;
border-bottom: #d0cece solid;
}
.paragraph {
display: block;
text-indent: 2em;
}
.comments {
margin-top: 20px;
}
.comment {
display: flex;
align-items: flex-start;
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: #eeeeee solid;
}
.comment-details {
flex: 1;
margin-left: 10px;
}
.comment-details .username {
font-weight: bold;
}
.comment-details .timestamp {
color: #888;
font-size: 12px;
}
.comment-content {
margin-top: 5px;
}
.comment-input {
position: fixed;/* 固定定位 */
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
padding: 10px;
background-color: #f9f9f9;
border-top: 1rpx solid #ddd;
z-index: 10;
}
.add-comment-btn {
background-color: transparent;
border: none;
font-size: 20px;
color: #007AFF;
margin-right: 10px;
}
.input-box {
flex: 1;
height: 30px;
padding: 0 10px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 6px;
}
.submit-button {
background-color: #007AFF;
color: white;
font-size: 14px;
padding: 5px 15px;
border-radius: 5px;
margin-left: 10px;
}
</style>

@ -0,0 +1,214 @@
<template>
<div class="question-list">
<ul>
<li
v-for="question in questions"
:key="question.uuid"
:class="['question-item', { selected: question.selected }]"
@click="handleSelect(question.id)"
>
<div class="info">
<span class="title">{{ question.name }}</span>
<div class="meta">
<span class="teacher">教师{{ question.teaname}}</span>
<span class="time">时间{{ moment(question.starttime).format('YYYY-MM-DD HH:mm:ss') }} - {{ moment(question.endtime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</div>
</div>
<div class="stats">
<span class="num">{{question.tmlist? JSON.parse(question.tmlist).length:0 }}</span>
<span class="total">总分{{ question.print }}</span>
</div>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import {onLoad} from "@dcloudio/uni-app";
import moment from "moment";
//
interface Question {
uuid: string
title: string
course: string
teacher: string
startTime: string
endTime: string
num: number
total: number
selected: boolean
}
//
const questions = ref([
{
uuid: '1',
title: '题目1',
course: '课程1',
teacher: '教师1',
startTime: '2023-10-01T08:00:00',
endTime: '2023-10-01T09:00:00',
num: 1,
total: 100,
selected: false
},
{
uuid: '2',
title: '题目2',
course: '课程2',
teacher: '教师2',
startTime: '2023-10-02T08:00:00',
endTime: '2023-10-02T09:00:00',
num: 2,
total: 200,
selected: false
}
])
onLoad((options)=>{
uni.request({
url: 'http://localhost:3400/api/timujistunew_ex',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
xuehao:'202413501062'
},
success: (res) => { /* ... */
console.log(res.data)
questions.value=res.data
}
});
})
//
const handleSelect = (uuid) => {
console.log(uuid)
uni.navigateTo({
url: '/pages/quiz_outclass/quiz_outclass?uuid='+uuid
});
}
//
const formatTime = (time: string): string => {
const date = new Date(time)
return `${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`
}
</script>
<style scoped>
.question-list {
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
min-height: 100vh;
padding: 40px 20px;
color: #fff;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.question-list h2 {
text-align: center;
font-size: 2rem;
margin-bottom: 20px;
color: #00ffff;
text-shadow: 0 0 10px #00ffff;
}
ul {
list-style-type: none;
padding: 0;
max-width: 900px;
margin: 0 auto;
}
.question-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
margin-bottom: 16px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
cursor: pointer;
transition: all 0.3s ease-in-out;
border-left: 4px solid transparent;
}
.question-item:hover {
transform: scale(1.02);
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 24px rgba(0, 255, 255, 0.2);
}
.question-item.selected {
background-color: rgba(0, 255, 255, 0.2);
border-left-color: #00ffff;
box-shadow: 0 0 10px #00ffff;
}
.info {
display: flex;
flex-direction: column;
}
.title {
font-size: 18px;
font-weight: bold;
color: #ffffff;
margin-bottom: 6px;
}
.meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
font-size: 12px;
color: #ccc;
}
.meta span {
padding: 4px 8px;
border-radius: 6px;
font-weight: 500;
}
.course {
background-color: rgba(0, 255, 255, 0.2);
color: #00ffff;
}
.teacher {
background-color: rgba(255, 255, 0, 0.2);
color: #fffa86;
}
.time {
background-color: rgba(255, 255, 255, 0.15);
color: #ddd;
width: 60vw;
}
.stats {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.num {
font-size: 12px;
background-color: rgba(0, 255, 127, 0.2);
color: #00ff7f;
padding: 4px 8px;
border-radius: 6px;
}
.total {
font-size: 12px;
background-color: rgba(255, 105, 180, 0.2);
color: #ff69b4;
padding: 4px 8px;
border-radius: 6px;
margin-top: 6px;
}
</style>

@ -0,0 +1,253 @@
<template>
<view class="tech-welcome-container">
<!-- 科技风背景元素 -->
<view class="tech-background">
<view class="tech-circle circle-1"></view>
<view class="tech-circle circle-2"></view>
<view class="tech-line line-1"></view>
<view class="tech-line line-2"></view>
<view class="tech-dots">
<view class="dot" v-for="i in 20" :key="i" :style="getDotStyle(i)"></view>
</view>
</view>
<!-- 主要内容区 -->
<view class="welcome-content">
<!-- 动态科技感标题 -->
<view class="tech-title">
<text class="title-text">只为遇见</text>
<text class="title-text highlight">更好的你</text>
</view>
<!-- 加载进度条 -->
<view class="tech-progress">
<view class="progress-bar">
<view class="progress-fill" :style="{width: progress + '%'}"></view>
</view>
<text class="progress-text">{{progress}}%</text>
</view>
</view>
<!-- 底部科技风装饰 -->
<view class="tech-footer">
<view class="footer-line"></view>
<text class="footer-text">智慧教育平台</text>
</view>
</view>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const progress = ref(0)
onMounted(() => {
//
const timer = setInterval(() => {
progress.value += Math.floor(Math.random() * 10) + 5
if (progress.value >= 100) {
progress.value = 100
clearInterval(timer)
//
uni.switchTab({
url: '/pages/learning/learning'
})
}
}, 300)
})
//
const getDotStyle = (index) => {
const size = Math.random() * 6 + 2
return {
width: `${size}px`,
height: `${size}px`,
left: `${Math.random() * 100}%`,
top: `${Math.random() * 100}%`,
opacity: Math.random() * 0.5 + 0.3,
animationDelay: `${index * 0.1}s`
}
}
</script>
<style lang="scss">
.tech-welcome-container {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(135deg, #0a0e21 0%, #121a3a 100%);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: hidden;
color: #fff;
}
/* 科技风背景元素 */
.tech-background {
position: absolute;
width: 100%;
height: 100%;
.tech-circle {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle, rgba(0, 240, 255, 0.05), transparent 70%);
&.circle-1 {
width: 600rpx;
height: 600rpx;
top: -300rpx;
right: -300rpx;
}
&.circle-2 {
width: 500rpx;
height: 500rpx;
bottom: -250rpx;
left: -250rpx;
}
}
.tech-line {
position: absolute;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.2), transparent);
&.line-1 {
top: 30%;
left: -100px;
right: -100px;
transform: rotate(-5deg);
}
&.line-2 {
top: 60%;
left: -100px;
right: -100px;
transform: rotate(5deg);
}
}
.tech-dots {
position: absolute;
width: 100%;
height: 100%;
.dot {
position: absolute;
background-color: #00f0ff;
border-radius: 50%;
animation: float 3s infinite ease-in-out;
}
}
}
.welcome-content {
position: relative;
width: 80%;
display: flex;
flex-direction: column;
align-items: center;
z-index: 1;
}
/* 科技感标题 */
.tech-title {
text-align: center;
margin-bottom: 80rpx;
.title-text {
display: block;
font-size: 48rpx;
font-weight: bold;
letter-spacing: 2rpx;
&.highlight {
background: linear-gradient(to right, #00f0ff, #0088ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
margin-top: 20rpx;
font-size: 56rpx;
}
}
}
/* 科技风进度条 */
.tech-progress {
width: 100%;
margin-top: 60rpx;
.progress-bar {
height: 8rpx;
background: rgba(255, 255, 255, 0.1);
border-radius: 4rpx;
overflow: hidden;
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.3), transparent);
animation: shine 2s infinite;
}
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #00f0ff, #0088ff);
border-radius: 4rpx;
transition: width 0.3s ease;
}
.progress-text {
display: block;
text-align: center;
margin-top: 20rpx;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
}
}
/* 底部装饰 */
.tech-footer {
position: absolute;
bottom: 60rpx;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
.footer-line {
width: 100px;
height: 2px;
background: linear-gradient(90deg, transparent, #00f0ff, transparent);
margin-bottom: 20rpx;
}
.footer-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
letter-spacing: 2rpx;
}
}
/* 动画效果 */
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
@keyframes shine {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
</style>

@ -0,0 +1,285 @@
<template>
<view class="tech-container">
<!-- 科技风背景装饰 -->
<view class="tech-bg">
<view class="tech-line line-1"></view>
<view class="tech-line line-2"></view>
<view class="tech-dot dot-1"></view>
<view class="tech-dot dot-2"></view>
</view>
<!-- 功能导航入口 -->
<view class="nav-grid">
<view
v-for="(item, index) in navItems"
:key="index"
class="nav-item"
@click="navigateTo(item.page)"
>
<view class="nav-icon" :style="{ background: item.bgColor }">
<!-- 使用uni-icons或图片作为图标 -->
<image v-if="item.icon.includes('.png')" :src="item.icon" mode="aspectFit" style="width: 32rpx; height: 32rpx;"/>
<uni-icons v-else :type="item.icon" size="40" color="#fff"></uni-icons>
</view>
<text class="nav-text">{{ item.name }}</text>
<text class="nav-action">立即进入</text>
</view>
</view>
<!-- 我的课程 -->
<view class="my-course">
<view class="section-header">
<text class="section-title">我的课程 (1)</text>
<text class="section-more">查看全部 </text>
</view>
<view class="course-card">
<image class="course-cover" src="/static/student-course/bg.png" mode="aspectFill"></image>
<view class="course-info">
<text class="course-name">军事理论</text>
<text class="course-subtitle">军事理论-综合版</text>
<view class="progress-bar">
<view class="progress-active" style="width: 100%"></view>
</view>
<text class="progress-text">已完成 100%</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
//
const navItems = ref([
{
name: '讨论',
icon: '/static/student-course/discussion.png', //
page: '/pages/discussion/discussion'
},
{
name: '签到',
icon: '/static/student-course/checkin.png',
page: '/pages/checkin/checkin'
},
{
name: '答题',
icon: '/static/student-course/quiz.png',
page: '/pages/quiz_outclass/selectquestion'
},
{
name: '考试',
icon: '/static/student-course/exam.png',
page: '/pages_A/exam/selectexam'
},
{
name: '扫码',
icon: '/static/student-course/scan.png',
page: 'scan'
}
])
//
const navigateTo = (url) => {
if(url === 'scan') {
uni.scanCode({
success: function (res) {
console.log('条码类型:' + res.scanType);
console.log('条码内容:' + res.result);
console.log('/pages/shangke/shangke?uuid='+JSON.parse(res.result).uuid+'&kch='+JSON.parse(res.result).kch+'&kctime='+JSON.parse(res.result).kctime)
uni.navigateTo({
url: '/pages/shangke/shangke?uuid='+JSON.parse(res.result).uuid+'&kch='+JSON.parse(res.result).kch+'&kctime='+JSON.parse(res.result).kctime
});
}
});
} else {
uni.navigateTo({
url: url
});
}
}
</script>
<style lang="scss">
.tech-container {
position: relative;
padding: 30rpx;
background-color: #0a0e21;
color: #fff;
min-height: 100vh;
overflow: hidden;
}
/* 科技风背景 */
.tech-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
overflow: hidden;
.tech-line {
position: absolute;
height: 1rpx;
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.2), transparent);
&.line-1 {
top: 30%;
left: -100rpx;
right: -100rpx;
transform: rotate(-5deg);
}
&.line-2 {
top: 60%;
left: -100rpx;
right: -100rpx;
transform: rotate(5deg);
}
}
.tech-dot {
position: absolute;
border-radius: 50%;
background-color: rgba(0, 240, 255, 0.05);
&.dot-1 {
width: 400rpx;
height: 400rpx;
top: -200rpx;
right: -200rpx;
}
&.dot-2 {
width: 300rpx;
height: 300rpx;
bottom: -150rpx;
left: -150rpx;
}
}
}
/* 导航网格 */
.nav-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 40rpx;
margin-bottom: 30rpx;
.nav-item {
position: relative;
background-color: rgba(20, 30, 50, 0.6);
border-radius: 16rpx;
padding: 30rpx;
border: 1rpx solid rgba(0, 240, 255, 0.1);
overflow: hidden;
transition: all 0.3s ease;
&:active {
transform: scale(0.98);
}
}
.nav-icon {
width: 75rpx;
height: 75rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.3);
}
.nav-text {
font-size: 28rpx;
font-weight: bold;
display: block;
margin-bottom: 10rpx;
}
.nav-action {
font-size: 24rpx;
color: #00f0ff;
}
}
/* 我的课程 */
.my-course {
background-color: rgba(20, 30, 50, 0.6);
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 30rpx;
border: 1rpx solid rgba(0, 240, 255, 0.1);
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
.section-title {
font-size: 32rpx;
font-weight: bold;
}
.section-more {
font-size: 24rpx;
color: #00f0ff;
}
}
.course-card {
display: flex;
background-color: rgba(0, 0, 0, 0.2);
border-radius: 12rpx;
overflow: hidden;
.course-cover {
width: 200rpx;
height: 150rpx;
}
.course-info {
flex: 1;
padding: 20rpx;
.course-name {
font-size: 28rpx;
font-weight: bold;
display: block;
margin-bottom: 5rpx;
}
.course-subtitle {
font-size: 24rpx;
color: #aaa;
display: block;
margin-bottom: 15rpx;
}
.progress-bar {
height: 8rpx;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 4rpx;
margin-bottom: 10rpx;
overflow: hidden;
.progress-active {
height: 100%;
background: linear-gradient(to right, #00f0ff, #0088ff);
border-radius: 4rpx;
}
}
.progress-text {
font-size: 22rpx;
color: #00f0ff;
}
}
}
}
</style>

@ -0,0 +1,26 @@
<template>
<view class="container">
<text class="title">请假</text>
<!-- 页面内容 -->
</view>
</template>
<script setup>
import {
ref
} from 'vue'
//
const pageTitle = ref('请假')
</script>
<style>
.container {
padding: 20px;
}
.title {
font-size: 18px;
color: #333;
}
</style>

@ -0,0 +1,10 @@
<script setup lang="ts">
import { ref } from 'vue'
</script>
<template>
<view>课程列表</view>
</template>
<style scoped>
</style>

@ -0,0 +1,396 @@
<template>
<view class="leave-page">
<!-- 表单区域 -->
<view class="form-section">
<view class="section-title">
<text class="title-text">请假申请</text>
<view class="title-decoration"></view>
</view>
<view class="form-container">
<view class="form-item">
<text class="item-label">请假类型</text>
<picker
mode="selector"
:range="leaveTypes"
range-key="name"
@change="handleTypeChange"
>
<view class="picker">
{{ selectedType.name || '请选择请假类型' }}
<uni-icons type="arrowdown" size="16" color="#4facfe"></uni-icons>
</view>
</picker>
</view>
<view class="form-item">
<text class="item-label">请假时间</text>
<view class="time-picker">
<picker
mode="date"
fields="datetime"
@change="handleStartTimeChange"
>
<view class="time-input">
{{ startTime || '开始时间' }}
</view>
</picker>
<text class="time-separator"></text>
<picker
mode="date"
fields="datetime"
@change="handleEndTimeChange"
>
<view class="time-input">
{{ endTime || '结束时间' }}
</view>
</picker>
</view>
</view>
<view class="form-item">
<text class="item-label">请假原因</text>
<textarea
v-model="reason"
placeholder="请输入请假原因"
class="textarea"
placeholder-class="placeholder"
></textarea>
</view>
<view class="form-item">
<text class="item-label">上传证明</text>
<uni-file-picker
limit="3"
title="最多选择3个文件"
file-mediatype="image"
@select="handleFileSelect"
></uni-file-picker>
</view>
</view>
<TechButton class="submit-btn" @click="handleSubmit"></TechButton>
</view>
<!-- 历史记录 -->
<view class="history-section">
<view class="section-title">
<text class="title-text">请假记录</text>
<view class="title-decoration"></view>
</view>
<view class="history-list">
<view
v-for="(item, index) in historyList"
:key="index"
class="history-item"
>
<view class="item-header">
<text class="type">{{ item.type }}</text>
<view class="status-badge" :class="item.status">
{{ item.statusText }}
</view>
</view>
<view class="item-content">
<view class="time-range">
<uni-icons type="calendar" size="16" color="#4facfe"></uni-icons>
<text>{{ item.startTime }} {{ item.endTime }}</text>
</view>
<text class="reason">{{ item.reason }}</text>
</view>
<view class="item-footer" v-if="item.status === 'approved'">
<text class="approver">审批人: {{ item.approver }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import TechButton from '@/components/managemet/TechButton.vue'
const leaveTypes = ref([
{ id: 1, name: '病假', icon: 'medkit' },
{ id: 2, name: '事假', icon: 'home' },
{ id: 3, name: '公假', icon: 'briefcase' },
{ id: 4, name: '其他', icon: 'help' }
])
const selectedType = ref({})
const startTime = ref('')
const endTime = ref('')
const reason = ref('')
const files = ref([])
const historyList = ref([
{
id: 1,
type: '病假',
startTime: '2023-05-10 08:00',
endTime: '2023-05-12 18:00',
reason: '感冒发烧体温38.5℃医生建议休息3天',
status: 'approved',
statusText: '已批准',
approver: '李老师'
},
{
id: 2,
type: '事假',
startTime: '2023-04-15 13:00',
endTime: '2023-04-15 17:00',
reason: '家中有急事需要处理',
status: 'rejected',
statusText: '已拒绝'
},
{
id: 3,
type: '公假',
startTime: '2023-03-20 09:00',
endTime: '2023-03-21 17:00',
reason: '参加学校组织的学术竞赛',
status: 'approved',
statusText: '已批准',
approver: '王老师'
}
])
const handleTypeChange = (e) => {
selectedType.value = leaveTypes.value[e.detail.value]
}
const handleStartTimeChange = (e) => {
startTime.value = e.detail.value
}
const handleEndTimeChange = (e) => {
endTime.value = e.detail.value
}
const handleFileSelect = (e) => {
files.value = e.tempFilePaths
}
const handleSubmit = () => {
if (!selectedType.value.id) {
uni.showToast({ title: '请选择请假类型', icon: 'none' })
return
}
if (!startTime.value || !endTime.value) {
uni.showToast({ title: '请选择请假时间', icon: 'none' })
return
}
if (!reason.value) {
uni.showToast({ title: '请输入请假原因', icon: 'none' })
return
}
uni.showLoading({ title: '提交中...' })
// API
setTimeout(() => {
uni.hideLoading()
uni.showToast({ title: '提交成功', icon: 'success' })
//
historyList.value.unshift({
id: Date.now(),
type: selectedType.value.name,
startTime: startTime.value,
endTime: endTime.value,
reason: reason.value,
status: 'pending',
statusText: '审核中'
})
//
selectedType.value = {}
startTime.value = ''
endTime.value = ''
reason.value = ''
files.value = []
}, 1500)
}
</script>
<style lang="scss" scoped>
.leave-page {
.section-title {
margin-bottom: 30rpx;
position: relative;
.title-text {
font-size: 32rpx;
color: #4facfe;
font-weight: 600;
position: relative;
display: inline-block;
padding-right: 20rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.title-decoration {
position: absolute;
bottom: -10rpx;
left: 0;
width: 60rpx;
height: 4rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
border-radius: 2rpx;
}
}
.form-container {
margin-bottom: 40rpx;
.form-item {
margin-bottom: 30rpx;
.item-label {
display: block;
font-size: 28rpx;
color: rgba(255, 255, 255, 0.8);
margin-bottom: 15rpx;
}
.picker {
height: 80rpx;
line-height: 80rpx;
padding: 0 20rpx;
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 28rpx;
}
.time-picker {
display: flex;
align-items: center;
gap: 20rpx;
.time-input {
flex: 1;
height: 80rpx;
line-height: 80rpx;
padding: 0 20rpx;
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
color: #fff;
font-size: 28rpx;
}
.time-separator {
color: rgba(255, 255, 255, 0.5);
font-size: 28rpx;
}
}
.textarea {
width: 100%;
height: 180rpx;
padding: 20rpx;
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
color: #fff;
font-size: 28rpx;
box-sizing: border-box;
}
.placeholder {
color: rgba(255, 255, 255, 0.3);
}
}
}
.submit-btn {
margin-top: 20rpx;
}
.history-list {
.history-item {
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
padding: 25rpx;
margin-bottom: 20rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
.item-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.type {
font-size: 28rpx;
color: #4facfe;
font-weight: 500;
}
.status-badge {
font-size: 24rpx;
padding: 5rpx 15rpx;
border-radius: 20rpx;
&.approved {
background: rgba(0, 200, 83, 0.15);
color: #00c853;
border: 1rpx solid rgba(0, 200, 83, 0.3);
}
&.rejected {
background: rgba(255, 82, 82, 0.15);
color: #ff5252;
border: 1rpx solid rgba(255, 82, 82, 0.3);
}
&.pending {
background: rgba(255, 171, 0, 0.15);
color: #ffab00;
border: 1rpx solid rgba(255, 171, 0, 0.3);
}
}
}
.item-content {
.time-range {
display: flex;
align-items: center;
gap: 10rpx;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.7);
margin-bottom: 15rpx;
}
.reason {
display: block;
font-size: 28rpx;
color: #fff;
line-height: 1.5;
}
}
.item-footer {
margin-top: 15rpx;
padding-top: 15rpx;
border-top: 1rpx solid rgba(74, 144, 226, 0.1);
.approver {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.5);
}
}
}
}
}
</style>

@ -0,0 +1,134 @@
<template>
<view class="tech-container">
<!-- 科技风格头部 -->
<view class="tech-header">
<view class="header-content">
<text class="header-title">课程管理中心</text>
<view class="header-subtitle">Course Management System</view>
</view>
<view class="header-decoration"></view>
</view>
<!-- 内容区域 -->
<view class="content-wrapper">
<!-- 导航卡片 -->
<view class="nav-grid">
<TechCard
v-for="(item, index) in navItems"
:key="index"
:title="item.title"
:icon="item.icon"
:active="activeIndex === index"
@click="switchTab(index)"
/>
</view>
<!-- 修改后的子页面容器 -->
<view class="page-container">
<LeavePage v-if="activeIndex === 0" />
<ProgramPage v-if="activeIndex === 1" />
<TranscriptPage v-if="activeIndex === 2" />
<SelectionPage v-if="activeIndex === 3" />
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import TechCard from '@/components/managemet/TechCard.vue'
import LeavePage from './leave.vue'
import ProgramPage from './program.vue'
import TranscriptPage from './transcript.vue'
import SelectionPage from './selection.vue'
//
const navItems = ref([
{ title: '请假申请', icon: 'calendar-remove' },
{ title: '培养方案', icon: 'book' },
{ title: '成绩单', icon: 'document-text' },
{ title: '选课中心', icon: 'checkmark-circle' }
])
const activeIndex = ref(0)
//
const switchTab = (index) => {
activeIndex.value = index
}
</script>
<style lang="scss" scoped>
.tech-container {
min-height: 100vh;
background: #0a0e17;
color: #fff;
font-family: 'PingFang SC', 'Helvetica Neue', sans-serif;
position: relative;
overflow: hidden;
}
.tech-header {
position: relative;
padding: 40rpx 30rpx 60rpx;
background: linear-gradient(135deg, #1a2a3a 0%, #0a0e17 100%);
z-index: 1;
overflow: hidden;
.header-content {
position: relative;
z-index: 2;
.header-title {
font-size: 42rpx;
font-weight: 600;
letter-spacing: 1rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: inline-block;
}
.header-subtitle {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
margin-top: 10rpx;
letter-spacing: 1rpx;
}
}
.header-decoration {
position: absolute;
top: 0;
right: 0;
width: 200rpx;
height: 200rpx;
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
z-index: 1;
}
}
.content-wrapper {
padding: 0 20rpx 40rpx;
position: relative;
z-index: 2;
margin-top: -30rpx;
}
.nav-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
margin-bottom: 30rpx;
}
.page-container {
background: rgba(16, 24, 40, 0.8);
border-radius: 16rpx;
padding: 30rpx;
min-height: 600rpx;
backdrop-filter: blur(10px);
border: 1px solid rgba(74, 144, 226, 0.2);
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.3);
}
</style>

@ -0,0 +1,289 @@
<template>
<view class="program-page">
<!-- 基本信息 -->
<view class="info-section">
<view class="section-title">
<text class="title-text">培养方案</text>
<view class="title-decoration"></view>
</view>
<view class="info-grid">
<view class="info-item">
<uni-icons type="medal" size="20" color="#4facfe"></uni-icons>
<text class="info-label">专业名称</text>
<text class="info-value">计算机科学与技术</text>
</view>
<view class="info-item">
<uni-icons type="flag" size="20" color="#4facfe"></uni-icons>
<text class="info-label">培养层次</text>
<text class="info-value">本科</text>
</view>
<view class="info-item">
<uni-icons type="calendar" size="20" color="#4facfe"></uni-icons>
<text class="info-label">学制</text>
<text class="info-value">4</text>
</view>
<view class="info-item">
<uni-icons type="star" size="20" color="#4facfe"></uni-icons>
<text class="info-label">毕业学分</text>
<text class="info-value">160学分</text>
</view>
</view>
</view>
<!-- 课程体系 -->
<view class="course-section">
<view class="section-title">
<text class="title-text">课程体系</text>
<view class="title-decoration"></view>
</view>
<uni-collapse>
<uni-collapse-item
v-for="(item, index) in courseSystem"
:key="index"
:title="item.name"
:show-animation="true"
:border="false"
>
<view class="course-list">
<view
v-for="(course, cIndex) in item.courses"
:key="cIndex"
class="course-item"
>
<text class="course-name">{{ course.name }}</text>
<view class="course-meta">
<text class="course-credit">{{ course.credit }}学分</text>
<text class="course-type">{{ course.type }}</text>
</view>
</view>
</view>
</uni-collapse-item>
</uni-collapse>
</view>
<!-- 学分进度 -->
<view class="progress-section">
<view class="section-title">
<text class="title-text">学分进度</text>
<view class="title-decoration"></view>
</view>
<view class="progress-list">
<view
v-for="(item, index) in creditProgress"
:key="index"
class="progress-item"
>
<view class="progress-header">
<text class="category">{{ item.category }}</text>
<text class="value">{{ item.completed }}/{{ item.total }}学分</text>
</view>
<TechProgress :percent="item.percent" />
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import TechProgress from '@/components/managemet/TechProgress.vue'
const courseSystem = ref([
{
name: '通识教育课程',
courses: [
{ name: '大学英语', credit: 8, type: '必修' },
{ name: '高等数学', credit: 10, type: '必修' },
{ name: '大学物理', credit: 6, type: '必修' },
{ name: '思想政治理论', credit: 12, type: '必修' },
{ name: '人文艺术选修', credit: 4, type: '选修' }
]
},
{
name: '专业基础课程',
courses: [
{ name: '程序设计基础', credit: 4, type: '必修' },
{ name: '数据结构', credit: 4, type: '必修' },
{ name: '计算机组成原理', credit: 4, type: '必修' },
{ name: '操作系统', credit: 4, type: '必修' },
{ name: '离散数学', credit: 3, type: '必修' }
]
},
{
name: '专业核心课程',
courses: [
{ name: '算法设计与分析', credit: 4, type: '必修' },
{ name: '计算机网络', credit: 4, type: '必修' },
{ name: '数据库系统', credit: 4, type: '必修' },
{ name: '软件工程', credit: 4, type: '必修' },
{ name: '人工智能基础', credit: 3, type: '选修' }
]
},
{
name: '实践环节',
courses: [
{ name: '课程设计', credit: 6, type: '必修' },
{ name: '毕业实习', credit: 4, type: '必修' },
{ name: '毕业论文', credit: 8, type: '必修' },
{ name: '创新创业实践', credit: 2, type: '选修' }
]
}
])
const creditProgress = ref([
{ category: '通识教育', completed: 32, total: 40, percent: 80 },
{ category: '专业基础', completed: 15, total: 20, percent: 75 },
{ category: '专业核心', completed: 8, total: 16, percent: 50 },
{ category: '实践环节', completed: 4, total: 20, percent: 20 }
])
</script>
<style lang="scss" scoped>
.program-page {
.section-title {
margin-bottom: 30rpx;
position: relative;
.title-text {
font-size: 32rpx;
color: #4facfe;
font-weight: 600;
position: relative;
display: inline-block;
padding-right: 20rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.title-decoration {
position: absolute;
bottom: -10rpx;
left: 0;
width: 60rpx;
height: 4rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
border-radius: 2rpx;
}
}
.info-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
margin-bottom: 40rpx;
.info-item {
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
padding: 25rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
.info-label {
display: block;
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
margin: 10rpx 0 5rpx;
}
.info-value {
display: block;
font-size: 28rpx;
color: #fff;
font-weight: 500;
}
}
}
.course-section {
margin-bottom: 40rpx;
:deep(.uni-collapse) {
background-color: transparent;
border: none;
}
:deep(.uni-collapse-item__title) {
background: rgba(16, 24, 40, 0.8);
color: #fff;
border-radius: 12rpx;
margin-bottom: 10rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
padding: 20rpx 25rpx;
}
:deep(.uni-collapse-item__wrap) {
background: rgba(16, 24, 40, 0.6);
border: none;
border-radius: 0 0 12rpx 12rpx;
}
.course-list {
padding: 0 25rpx 20rpx;
.course-item {
padding: 20rpx 0;
border-bottom: 1rpx solid rgba(74, 144, 226, 0.1);
&:last-child {
border-bottom: none;
}
.course-name {
display: block;
font-size: 28rpx;
color: #fff;
margin-bottom: 10rpx;
}
.course-meta {
display: flex;
justify-content: space-between;
.course-credit {
font-size: 24rpx;
color: #4facfe;
}
.course-type {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
}
}
}
}
.progress-list {
.progress-item {
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
padding: 25rpx;
margin-bottom: 20rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
.progress-header {
display: flex;
justify-content: space-between;
margin-bottom: 15rpx;
.category {
font-size: 28rpx;
color: #fff;
}
.value {
font-size: 26rpx;
color: #4facfe;
}
}
}
}
}
</style>

@ -0,0 +1,614 @@
<template>
<view class="selection-page">
<!-- 头部信息 -->
<view class="header-section">
<view class="header-content">
<text class="title">选课中心</text>
<text class="subtitle">Course Selection System</text>
</view>
<view class="header-decoration"></view>
</view>
<!-- 选课状态卡片 -->
<view class="status-cards">
<view class="status-card">
<view class="card-content">
<text class="card-label">可选学分</text>
<text class="card-value">{{ availableCredits }}</text>
<text class="card-unit">学分</text>
</view>
<view class="card-decoration"></view>
</view>
<view class="status-card">
<view class="card-content">
<text class="card-label">已选学分</text>
<text class="card-value">{{ selectedCredits }}</text>
<text class="card-unit">学分</text>
</view>
<view class="card-decoration"></view>
</view>
</view>
<!-- 选课进度 -->
<view class="progress-section">
<view class="section-header">
<text class="section-title">选课进度</text>
<view class="section-decoration"></view>
</view>
<TechProgress :percent="selectionProgress" />
</view>
<!-- 课程筛选 -->
<view class="filter-section">
<view class="filter-tabs">
<view
v-for="(tab, index) in tabs"
:key="index"
class="filter-tab"
:class="{ active: activeTab === index }"
@click="changeTab(index)"
>
<text>{{ tab }}</text>
<view class="tab-indicator"></view>
</view>
</view>
<view class="filter-options">
<picker
mode="selector"
:range="semesters"
@change="handleSemesterChange"
>
<view class="filter-option">
<text>{{ currentSemester || '选择学期' }}</text>
<uni-icons type="arrowdown" size="16" color="#4facfe"></uni-icons>
</view>
</picker>
<picker
mode="selector"
:range="categories"
range-key="name"
@change="handleCategoryChange"
>
<view class="filter-option">
<text>{{ currentCategory.name || '课程类别' }}</text>
<uni-icons type="arrowdown" size="16" color="#4facfe"></uni-icons>
</view>
</picker>
</view>
</view>
<!-- 课程列表 -->
<view class="course-list">
<view
v-for="(course, index) in filteredCourses"
:key="index"
class="course-card"
>
<view class="course-header">
<text class="course-name">{{ course.name }}</text>
<text class="course-credit">{{ course.credit }}学分</text>
</view>
<view class="course-meta">
<view class="meta-item">
<uni-icons type="person" size="16" color="rgba(255,255,255,0.6)"></uni-icons>
<text>{{ course.teacher }}</text>
</view>
<view class="meta-item">
<uni-icons type="calendar" size="16" color="rgba(255,255,255,0.6)"></uni-icons>
<text>{{ course.time }}</text>
</view>
<view class="meta-item">
<uni-icons type="location" size="16" color="rgba(255,255,255,0.6)"></uni-icons>
<text>{{ course.location }}</text>
</view>
</view>
<view class="course-footer">
<text class="remaining">剩余名额: {{ course.remaining }}/{{ course.capacity }}</text>
<TechButton
size="small"
:type="course.selected ? 'secondary' : 'primary'"
@click="toggleSelection(course)"
>
{{ course.selected ? '已选' : '选课' }}
</TechButton>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="action-bar">
<view class="selected-info">
<text>已选 {{ selectedCount }} 门课程</text>
<text>{{ selectedCredits }}学分</text>
</view>
<TechButton @click="handleSubmit"></TechButton>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import TechProgress from '@/components/managemet/TechProgress.vue'
import TechButton from '@/components/managemet/TechButton.vue'
//
const availableCredits = ref(25)
const selectedCredits = ref(0)
const selectedCount = ref(0)
//
const tabs = ref(['全部课程', '专业选修', '公共选修', '通识教育'])
const activeTab = ref(0)
const semesters = ref(['2023-2024学年 第一学期', '2022-2023学年 第二学期', '2022-2023学年 第一学期'])
const currentSemester = ref('2023-2024学年 第一学期')
const categories = ref([
{ id: 1, name: '全部类别' },
{ id: 2, name: '人文社科' },
{ id: 3, name: '自然科学' },
{ id: 4, name: '工程技术' },
{ id: 5, name: '艺术体育' }
])
const currentCategory = ref({})
//
const courses = ref([
{
id: 1,
name: '人工智能基础',
credit: 3,
teacher: '张教授',
time: '周一 3-4节',
location: '信息楼201',
remaining: 15,
capacity: 60,
category: '工程技术',
type: '专业选修',
selected: false
},
{
id: 2,
name: '数据可视化',
credit: 2,
teacher: '李副教授',
time: '周三 5-6节',
location: '计算机中心305',
remaining: 8,
capacity: 40,
category: '工程技术',
type: '专业选修',
selected: false
},
{
id: 3,
name: '西方艺术史',
credit: 2,
teacher: '王教授',
time: '周二 7-8节',
location: '人文楼101',
remaining: 5,
capacity: 80,
category: '人文社科',
type: '公共选修',
selected: false
},
{
id: 4,
name: '环境科学导论',
credit: 2,
teacher: '赵教授',
time: '周四 1-2节',
location: '理学院203',
remaining: 12,
capacity: 100,
category: '自然科学',
type: '通识教育',
selected: false
},
{
id: 5,
name: '羽毛球',
credit: 1,
teacher: '陈老师',
time: '周五 5-6节',
location: '体育馆A',
remaining: 3,
capacity: 30,
category: '艺术体育',
type: '公共选修',
selected: false
}
])
//
const selectionProgress = computed(() => {
return Math.round((selectedCredits.value / availableCredits.value) * 100)
})
const filteredCourses = computed(() => {
let result = [...courses.value]
//
if (activeTab.value > 0) {
result = result.filter(course => course.type === tabs.value[activeTab.value])
}
//
if (currentCategory.value.id > 1) {
result = result.filter(course => course.category === currentCategory.value.name)
}
return result
})
//
const changeTab = (index) => {
activeTab.value = index
}
const handleSemesterChange = (e) => {
currentSemester.value = semesters.value[e.detail.value]
}
const handleCategoryChange = (e) => {
currentCategory.value = categories.value[e.detail.value]
}
const toggleSelection = (course) => {
if (course.selected) {
//
course.selected = false
selectedCredits.value -= course.credit
selectedCount.value--
course.remaining++
} else {
//
if (selectedCredits.value + course.credit > availableCredits.value) {
uni.showToast({
title: '已超过可选学分限制',
icon: 'none'
})
return
}
//
if (course.remaining <= 0) {
uni.showToast({
title: '该课程已无剩余名额',
icon: 'none'
})
return
}
//
course.selected = true
selectedCredits.value += course.credit
selectedCount.value++
course.remaining--
uni.showToast({
title: '选课成功',
icon: 'success'
})
}
}
const handleSubmit = () => {
if (selectedCount.value === 0) {
uni.showToast({
title: '请至少选择一门课程',
icon: 'none'
})
return
}
uni.showLoading({ title: '提交中...' })
// API
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '选课提交成功',
icon: 'success'
})
//
courses.value.forEach(course => {
if (course.selected) {
course.capacity--
course.selected = false
}
})
selectedCredits.value = 0
selectedCount.value = 0
}, 1500)
}
</script>
<style lang="scss" scoped>
.selection-page {
padding-bottom: 120rpx;
background: linear-gradient(to bottom, #0a0e17 0%, #1a2a3a 100%);
min-height: 100vh;
}
.header-section {
position: relative;
padding: 40rpx 30rpx 30rpx;
overflow: hidden;
.header-content {
position: relative;
z-index: 2;
.title {
font-size: 42rpx;
font-weight: 600;
background: linear-gradient(to right, #00f2fe, #4facfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
margin-top: 8rpx;
}
}
.header-decoration {
position: absolute;
top: 0;
right: 0;
width: 200rpx;
height: 200rpx;
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
z-index: 1;
}
}
.status-cards {
display: flex;
gap: 20rpx;
padding: 0 30rpx;
margin-bottom: 30rpx;
.status-card {
flex: 1;
background: rgba(16, 24, 40, 0.8);
border-radius: 16rpx;
padding: 25rpx;
position: relative;
overflow: hidden;
border: 1rpx solid rgba(74, 144, 226, 0.3);
.card-content {
position: relative;
z-index: 2;
.card-label {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.7);
display: block;
margin-bottom: 10rpx;
}
.card-value {
font-size: 40rpx;
font-weight: 600;
color: #fff;
display: block;
background: linear-gradient(to right, #00f2fe, #4facfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.card-unit {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
}
.card-decoration {
position: absolute;
bottom: -20rpx;
right: -20rpx;
width: 80rpx;
height: 80rpx;
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
z-index: 1;
}
}
}
.progress-section {
padding: 0 30rpx;
margin-bottom: 30rpx;
.section-header {
margin-bottom: 20rpx;
position: relative;
.section-title {
font-size: 32rpx;
font-weight: 500;
color: #4facfe;
position: relative;
display: inline-block;
padding-right: 20rpx;
}
.section-decoration {
position: absolute;
bottom: -8rpx;
left: 0;
width: 60rpx;
height: 4rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
border-radius: 2rpx;
}
}
}
.filter-section {
padding: 0 30rpx;
margin-bottom: 30rpx;
.filter-tabs {
display: flex;
margin-bottom: 25rpx;
border-bottom: 1rpx solid rgba(74, 144, 226, 0.2);
.filter-tab {
padding: 15rpx 25rpx;
position: relative;
text {
font-size: 28rpx;
color: rgba(255, 255, 255, 0.6);
}
.tab-indicator {
position: absolute;
bottom: -1rpx;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 4rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
border-radius: 2rpx;
transition: width 0.3s ease;
}
&.active {
text {
color: #fff;
font-weight: 500;
}
.tab-indicator {
width: 60rpx;
}
}
}
}
.filter-options {
display: flex;
gap: 20rpx;
.filter-option {
flex: 1;
height: 70rpx;
line-height: 70rpx;
padding: 0 25rpx;
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
color: #fff;
font-size: 26rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
}
}
.course-list {
padding: 0 30rpx;
.course-card {
background: rgba(16, 24, 40, 0.8);
border-radius: 16rpx;
padding: 25rpx;
margin-bottom: 20rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
.course-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.course-name {
font-size: 30rpx;
color: #fff;
font-weight: 500;
}
.course-credit {
font-size: 26rpx;
color: #4facfe;
font-weight: 500;
}
}
.course-meta {
margin-bottom: 25rpx;
.meta-item {
display: flex;
align-items: center;
gap: 10rpx;
margin-bottom: 15rpx;
text {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.7);
}
}
}
.course-footer {
display: flex;
justify-content: space-between;
align-items: center;
.remaining {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
}
}
}
.action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
background: rgba(16, 24, 40, 0.95);
backdrop-filter: blur(10px);
border-top: 1rpx solid rgba(74, 144, 226, 0.3);
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
.selected-info {
display: flex;
flex-direction: column;
text {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.8);
&:last-child {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
}
}
:deep(.tech-button) {
width: 200rpx;
}
}
</style>

@ -0,0 +1,450 @@
<template>
<view class="transcript-page">
<!-- 头部信息 -->
<view class="header-section">
<view class="header-content">
<text class="title">成绩单</text>
<text class="subtitle">Academic Transcript</text>
</view>
<view class="header-decoration"></view>
</view>
<!-- 学期选择器 -->
<view class="semester-selector">
<picker
mode="selector"
:range="semesters"
range-key="name"
@change="handleSemesterChange"
>
<view class="selector-box">
<text class="current-semester">{{ currentSemester.name }}</text>
<uni-icons type="arrowdown" size="18" color="#4facfe"></uni-icons>
</view>
</picker>
</view>
<!-- GPA统计卡片 -->
<view class="stats-grid">
<view class="stat-card">
<view class="stat-content">
<text class="stat-label">学期GPA</text>
<text class="stat-value">{{ currentSemester.gpa }}</text>
<view class="stat-trend" :class="gpaTrendClass">
<uni-icons :type="gpaTrendIcon" size="16" color="currentColor"></uni-icons>
<text>{{ gpaTrendText }}</text>
</view>
</view>
<view class="stat-decoration"></view>
</view>
<view class="stat-card">
<view class="stat-content">
<text class="stat-label">累计GPA</text>
<text class="stat-value">{{ cumulativeGPA }}</text>
<text class="stat-meta">专业排名 {{ rank }}</text>
</view>
<view class="stat-decoration"></view>
</view>
</view>
<!-- 成绩列表 -->
<view class="grades-section">
<view class="section-header">
<text class="section-title">课程成绩</text>
<view class="section-decoration"></view>
</view>
<view class="grades-list">
<view
v-for="(course, index) in currentSemester.courses"
:key="index"
class="grade-item"
>
<view class="course-info">
<text class="course-name">{{ course.name }}</text>
<text class="course-credit">{{ course.credit }}学分</text>
</view>
<view class="grade-score" :class="getScoreClass(course.score)">
<text>{{ course.score }}</text>
<text class="grade-level">{{ getGradeLevel(course.score) }}</text>
</view>
</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view class="action-buttons">
<TechButton type="primary" @click="handleDownload">
<uni-icons type="download" size="18" color="#fff"></uni-icons>
<text>下载成绩单</text>
</TechButton>
<TechButton type="secondary" @click="handleShare">
<uni-icons type="share" size="18" color="#4facfe"></uni-icons>
<text>分享成绩</text>
</TechButton>
</view>
</view>
</template>
<script setup>
import { ref, computed } from 'vue'
import TechButton from '@/components/managemet/TechButton.vue'
const semesters = ref([
{
id: 1,
name: '2023-2024学年 第一学期',
gpa: '3.82',
trend: 'up',
courses: [
{ name: '人工智能原理', credit: 4, score: 94 },
{ name: '机器学习', credit: 4, score: 89 },
{ name: '大数据技术', credit: 3, score: 91 },
{ name: '计算机视觉', credit: 3, score: 87 },
{ name: '自然语言处理', credit: 3, score: 92 }
]
},
{
id: 2,
name: '2022-2023学年 第二学期',
gpa: '3.75',
trend: 'up',
courses: [
{ name: '操作系统', credit: 4, score: 88 },
{ name: '计算机网络', credit: 4, score: 85 },
{ name: '数据库系统', credit: 4, score: 90 },
{ name: '软件工程', credit: 3, score: 87 },
{ name: '编译原理', credit: 4, score: 83 }
]
},
{
id: 3,
name: '2022-2023学年 第一学期',
gpa: '3.68',
trend: 'down',
courses: [
{ name: '数据结构', credit: 4, score: 85 },
{ name: '算法分析', credit: 4, score: 82 },
{ name: '计算机组成', credit: 4, score: 84 },
{ name: '离散数学', credit: 3, score: 79 },
{ name: '概率统计', credit: 3, score: 88 }
]
}
])
const currentSemesterIndex = ref(0)
const currentSemester = computed(() => semesters.value[currentSemesterIndex.value])
const cumulativeGPA = computed(() => '3.75')
const rank = computed(() => '8/120')
// GPA
const gpaTrendIcon = computed(() => {
return currentSemester.value.trend === 'up' ? 'arrow-up' : 'arrow-down'
})
const gpaTrendClass = computed(() => {
return currentSemester.value.trend === 'up' ? 'trend-up' : 'trend-down'
})
const gpaTrendText = computed(() => {
return currentSemester.value.trend === 'up' ? '较上学期提升' : '较上学期下降'
})
//
const getGradeLevel = (score) => {
if (score >= 90) return 'A'
if (score >= 80) return 'B'
if (score >= 70) return 'C'
if (score >= 60) return 'D'
return 'F'
}
const getScoreClass = (score) => {
if (score >= 90) return 'excellent'
if (score >= 80) return 'good'
if (score >= 70) return 'average'
if (score >= 60) return 'pass'
return 'fail'
}
//
const handleSemesterChange = (e) => {
currentSemesterIndex.value = e.detail.value
}
const handleDownload = () => {
uni.showLoading({ title: '生成成绩单中...' })
setTimeout(() => {
uni.hideLoading()
uni.showToast({
title: '成绩单已保存到相册',
icon: 'success'
})
}, 1500)
}
const handleShare = () => {
uni.showActionSheet({
itemList: ['微信好友', 'QQ好友', '保存图片'],
success: (res) => {
uni.showToast({
title: `已分享到${['微信好友', 'QQ好友', '保存图片'][res.tapIndex]}`,
icon: 'success'
})
}
})
}
</script>
<style lang="scss" scoped>
.transcript-page {
padding-bottom: 40rpx;
background: linear-gradient(to bottom, #0a0e17 0%, #1a2a3a 100%);
min-height: 100vh;
}
.header-section {
position: relative;
padding: 40rpx 30rpx 30rpx;
overflow: hidden;
.header-content {
position: relative;
z-index: 2;
.title {
font-size: 42rpx;
font-weight: 600;
background: linear-gradient(to right, #00f2fe, #4facfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
margin-top: 8rpx;
}
}
.header-decoration {
position: absolute;
top: 0;
right: 0;
width: 200rpx;
height: 200rpx;
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
z-index: 1;
}
}
.semester-selector {
padding: 0 30rpx;
margin-bottom: 30rpx;
.selector-box {
height: 80rpx;
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
border: 1rpx solid rgba(74, 144, 226, 0.3);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 25rpx;
.current-semester {
font-size: 28rpx;
color: #fff;
}
}
}
.stats-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20rpx;
padding: 0 30rpx;
margin-bottom: 40rpx;
.stat-card {
background: rgba(16, 24, 40, 0.8);
border-radius: 16rpx;
padding: 25rpx;
position: relative;
overflow: hidden;
border: 1rpx solid rgba(74, 144, 226, 0.3);
.stat-content {
position: relative;
z-index: 2;
.stat-label {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.7);
display: block;
margin-bottom: 10rpx;
}
.stat-value {
font-size: 40rpx;
font-weight: 600;
color: #fff;
display: block;
margin-bottom: 15rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.stat-trend {
font-size: 24rpx;
display: flex;
align-items: center;
gap: 8rpx;
&.trend-up {
color: #00c853;
}
&.trend-down {
color: #ff5252;
}
}
.stat-meta {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
}
.stat-decoration {
position: absolute;
bottom: -20rpx;
right: -20rpx;
width: 80rpx;
height: 80rpx;
background: radial-gradient(circle, rgba(74, 144, 226, 0.2) 0%, transparent 70%);
z-index: 1;
}
}
}
.grades-section {
padding: 0 30rpx;
.section-header {
margin-bottom: 25rpx;
position: relative;
.section-title {
font-size: 32rpx;
font-weight: 500;
color: #4facfe;
position: relative;
display: inline-block;
padding-right: 20rpx;
}
.section-decoration {
position: absolute;
bottom: -8rpx;
left: 0;
width: 60rpx;
height: 4rpx;
background: linear-gradient(to right, #00f2fe, #4facfe);
border-radius: 2rpx;
}
}
.grades-list {
.grade-item {
background: rgba(16, 24, 40, 0.8);
border-radius: 12rpx;
padding: 25rpx;
margin-bottom: 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
border: 1rpx solid rgba(74, 144, 226, 0.3);
.course-info {
flex: 1;
.course-name {
font-size: 28rpx;
color: #fff;
display: block;
margin-bottom: 10rpx;
}
.course-credit {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.6);
}
}
.grade-score {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-weight: 600;
&.excellent {
background: rgba(0, 200, 83, 0.15);
border: 2rpx solid rgba(0, 200, 83, 0.3);
color: #00c853;
}
&.good {
background: rgba(74, 144, 226, 0.15);
border: 2rpx solid rgba(74, 144, 226, 0.3);
color: #4facfe;
}
&.average {
background: rgba(255, 171, 0, 0.15);
border: 2rpx solid rgba(255, 171, 0, 0.3);
color: #ffab00;
}
&.pass {
background: rgba(255, 82, 82, 0.15);
border: 2rpx solid rgba(255, 82, 82, 0.3);
color: #ff5252;
}
&.fail {
background: rgba(255, 82, 82, 0.3);
border: 2rpx solid rgba(255, 82, 82, 0.5);
color: #ff5252;
}
.grade-level {
font-size: 24rpx;
margin-top: 5rpx;
}
}
}
}
}
.action-buttons {
display: flex;
gap: 20rpx;
padding: 0 30rpx;
margin-top: 40rpx;
:deep(.tech-button) {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
gap: 10rpx;
}
}
</style>

@ -0,0 +1,26 @@
<template>
<view class="container">
<text class="title">培养方案</text>
<!-- 页面内容 -->
</view>
</template>
<script setup>
import {
ref
} from 'vue'
//
const pageTitle = ref('培养方案')
</script>
<style>
.container {
padding: 20px;
}
.title {
font-size: 18px;
color: #333;
}
</style>

@ -0,0 +1,563 @@
<template>
<view class="quiz-container">
<!-- 动态渲染题目 -->
<view :key="question.uuid" class="question">
<view >
<text class="time-wb" v-if="dati_able===true">{{ moment.utc(totaltime * 1000).format('mm:ss') }}</text>
<text class="time-wb" v-if="dati_able===false">{{ tijiao_type }}</text>
<image :src="question.pic" mode="aspectFit" class="question-image" />
<!-- 单选题刘东阳 -->
<view v-if="question.tx==='单选题'">
<view class="grid-container">
<view v-for="(item, index) in zm.slice(0,question.xuexiangnum)" :key="index" class="grid-item">
<button
:style="{backgroundColor:dx===item?'blue':''}"
class="choose-button"
@click="dx_one(item)"
>
{{ item }}
</button>
</view>
</view>
</view>
<view v-if="question.tx==='多选题'">
<view class="grid-container">
<view v-for="(item, index) in zm.slice(0,question.xuexiangnum)" :key="index" class="grid-item">
<button
:style="{backgroundColor:moredx.includes(item)?'blue':''}"
class="choose-button"
@click="moredx_one(item)"
>
{{ item }}
</button>
</view>
</view>
</view>
<view v-if="question.tx==='判断题'">
<view class="grid-container-pd" >
<view key="2" class="grid-item">
<button
:style="{backgroundColor:pdt==='对'?'blue':''}"
class="choose-button"
@click="pdt_one('对')"
>
</button>
</view>
<view key="index" class="grid-item">
</view>
<view key="index+1" class="grid-item">
<button
:style="{backgroundColor:pdt==='错'?'blue':''}"
class="choose-button"
@click="pdt_one('错')"
>
×
</button>
</view>
</view>
</view>
<view v-if="question.tx==='填空题'">
<view v-for="(tkxone,index_tkx) in question.tk" :key="index_tkx" >
<view class="xuhao">
<h3 class="xuhao_wb">{{index_tkx+1}}</h3>
</view>
<input :value="tkt[index_tkx]" @input="val=>{tkt_one_get(val.detail.value,index_tkx);}" type="text" class="tkt-input" :disabled="dati_able===false"/>
<text class="print-wb">{{tkxone.print}}</text>
</view>
</view>
<!-- 填空题 -->
<input v-if="question.type === 'fill'" v-model="userAnswers[question.id]" placeholder="请输入答案" />
<!-- 单选题 -->
<radio-group v-if="question.type === 'single'" :modelValue="userAnswers[question.id]"
@update:modelValue="handleRadioChange(question.id, $event)">
<label v-for="(option, i) in question.options" :key="i">
<radio :value="option.text[0]" />
<image v-if="option.image" :src="option.image" mode="aspectFit" class="option-image" />
<span v-else>{{ option.text }}</span>
</label>
</radio-group>
<!-- 多选题 -->
<checkbox-group v-if="question.type === 'multiple'" :modelValue="userAnswers[question.id]"
@update:modelValue="handleCheckboxChange(question.id, $event)">
<label v-for="(option, i) in question.options" :key="i">
<checkbox :value="option.text[0]" />
<image v-if="option.image" :src="option.image" mode="aspectFit" class="option-image" />
<span v-else>{{ option.text }}</span>
</label>
</checkbox-group>
<!-- 判断题 -->
<radio-group v-if="question.type === 'true-false'" :modelValue="userAnswers[question.id]"
@update:modelValue="handleRadioChange(question.id, $event)">
<label>
<radio value="true" /> 正确
</label>
<label>
<radio value="false" /> 错误
</label>
</radio-group>
</view>
</view>
<!-- 控制按钮 -->
<view class="controls">
<view v-if="tijiao_type==='正常做答'">
<button @click="submitAnswers" type="primary">提交答案</button>
</view>
<view v-if="tijiao_type==='已结束'">
<button type="primary">已结束</button>
</view>
<view v-if="tijiao_type==='已提交'">
<button type="primary">已提交</button>
</view>
</view>
<!-- 答案反馈 -->
<view v-if="showResult" class="result">
<text v-for="(res, key) in results" :key="key" :style="{ color: res ? 'green' : 'red' }">
{{ key + 1 }} {{ res ? '✅ 正确' : '❌ 错误' }}
</text>
</view>
</view>
</template>
<script setup lang="ts">
import {onMounted, onUnmounted, ref} from 'vue'
import {onLoad} from "@dcloudio/uni-app";
import moment from "moment";
const zm=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
const dx=ref('0');
const moredx=ref([])
const pdt=ref("none")
const tkt=ref([])
const uid=ref('122')
const dati_able=ref(true)
const intervalId = ref<number | null>(null); //
const endtime=ref('')
const totaltime=ref(12)
const tijiao_type=ref('正常做答')//,
const question=ref({
uuid: "e87ed72f-cecc-4ebd-9f08-34e24730797f",
px: 7,
kch: "202413501",
tea: "2024101",
title: "填空题APPPEERF",
infor: "{\"pic\":\"http://localhost:89/2024101/c2b9cfd7-1d46-45a5-bb02-ab701ab6aa84.png\",\"tk\":[{\"daan\":\"A\",\"print\":3},{\"daan\":\"B\",\"print\":4}],\"morechoose\":[\"A\"],\"onechoose\":\"A\",\"xuexiangnum\":4,\"zitimu\":[{\"pic\":\"http://localhost:89/tmbefore.png\",\"tk\":[{\"daan\":\"\",\"print\":1}],\"morechoose\":[\"A\"],\"onechoose\":\"A\",\"xuexiangnum\":4,\"title\":\"\"}],\"uuid\":\"e87ed72f-cecc-4ebd-9f08-34e24730797f\",\"tx\":\"填空题\",\"title\":\"填空题APPPEERF\"}",
kctime: "n8z1A1",
pic: "http://localhost:89/2024101/c2b9cfd7-1d46-45a5-bb02-ab701ab6aa84.png",
tk: [
{
daan: "A",
print: 3
},
{
daan: "B",
print: 4
}
],
morechoose: [
"A"
],
onechoose: "A",
xuexiangnum: 10,
zitimu: [
],
tx: "填空题"
})
//
function dx_one(xx:string){
if(dati_able) {
dx.value = xx
}
}
function tkt_one_get(xx:string,index:number){
if(dati_able) {
tkt.value[index] = xx;
}
}
onLoad((options) => {
console.log('load', options)
if(options) {
uid.value=options.uuid;
console.log('options:', options.uuid)
uni.request({
url: 'http://localhost:3400/apistu/getinclassactive',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: options.uuid
},
success: (res) => { /* ... */
console.log(res.data)
let infor = JSON.parse(res.data.infor)
console.log("题目",infor)
console.log(infor.endtime)
endtime.value = infor.endtime
question.value=infor.tm
}
});
}
uni.request({
url: 'http://localhost:3400/apistu/getinclassactivestu_ex',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: options.uuid,
xuehao:'202413501062'
},
success: (res) => { /* ... */
console.log(res.data)
if(res.data.length>0) {
stopTimer();
dati_able.value=false
tijiao_type.value="已提交"
console.log(res.data)
}
}
});
if(question.value.tx==="填空题") {
tkt.value = new Array(question.value.tk.length).fill('');
}
})
function moredx_one(xx:string){
if(dati_able) {
if (moredx.value.includes(xx)) {
moredx.value = moredx.value.filter(x => x.value !== xx)
} else {
moredx.value.push(xx)
}
}
}
const startTimer = () => {
intervalId.value = setInterval(() => {
if (totaltime.value >0&&tijiao_type.value!=="已提交") {
totaltime.value=moment(endtime.value).diff(moment(),'seconds')
} else {
stopTimer();
dati_able.value=false
tijiao_type.value='已结束'
}
}, 1000);
};
//
const stopTimer = () => {
if (intervalId.value !== null) {
clearInterval(intervalId.value);
intervalId.value = null;
}
};
function pdt_one(xx:string){
if(dati_able) {
pdt.value = xx
}
}
const quizData = [
{
id: 1,
type: 'fill',
questionImage: '/static/student-course/bg.png',
answer: '前端界面'
},
{
id: 2,
type: 'single',
questionImage: '/static/student-course/bg.png',
options: [
{ text: 'Vue Router', image: '/static/student-course/bg.png' },
{ text: 'Vuex', image: '/static/student-course/bg.png' },
{ text: 'Axios', image: '/static/student-course/bg.png' },
{ text: 'Vue', image: '/static/student-course/bg.png' }
],
answer: 'C'
},
{
id: 3,
type: 'multiple',
questionImage: '/static/student-course/bg.png',
options: [
{ text: 'ref', image: '/static/student-course/bg.png' },
{ text: 'reactive', image: '/static/student-course/bg.png' },
{ text: 'toRef', image: '/static/student-course/bg.png' },
{ text: 'defineProps', image: '/static/student-course/bg.png' }
],
answer: ['A', 'B', 'C']
},
{
id: 4,
type: 'true-false',
questionImage: '/static/student-course/bg.png',
answer: 'true'
}
]
const activeIndex = ref(0)
const showResult = ref(false)
// ID
const userAnswers = ref<Record<number, any>>({})
const results = ref<boolean[]>([])
function handleRadioChange(id: number, value: string) {
userAnswers.value[id] = value
}
function handleCheckboxChange(id: number, value: string[]) {
userAnswers.value[id] = value
}
function prevQuestion() {
if (activeIndex.value > 0) activeIndex.value--
}
function nextQuestion() {
if (activeIndex.value < quizData.length - 1) activeIndex.value++
}
function resetQuiz() {
userAnswers.value = {}
showResult.value = false
activeIndex.value = 0
}
function submitAnswers() {
let resulttm={};
if(question.value.tx==='填空题'){
let result=[];
let print_all=0;
let print_stu=0;
console.log()
question.value.tk.map((tkone,indextk)=>{
let resultone={};
resultone.studaan=tkt.value[indextk];
resultone.stddaan=tkone.daan
if(tkone.daan===tkt.value[indextk]){
resultone.stuprint=tkone.print;
resultone.stdprint=tkone.print
print_stu+=tkone.print
print_all+=tkone.print
}else {
resultone.stuprint=0;
resultone.stdprint=tkone.print
print_stu+=0
print_all+=tkone.print
}
result.push(resultone)
})
resulttm={stu:print_stu,all:print_all,tx:question.value.tx,tkt:result}
console.log(resulttm)
}else if(question.value.tx==='多选题'){
let zq=true;
question.value.morechoose.map((choose_one)=>{
if(!moredx.value.includes(choose_one)){
zq=false;
}
})
if(question.value.morechoose.length!==moredx.value.length){
zq=false;
}
let dxt={stu:moredx.value,all:question.value.morechoose}
if(zq){
resulttm.stu=question.value.print
resulttm.all=question.value.print
resulttm.dxt=dxt
}else{
resulttm.stu=0
resulttm.all=question.value.print
resulttm.dxt=dxt
}
console.log(resulttm)
}else if(question.value.tx==='单选题'){
let zq=true;
if(dx.value!==question.value.onechoose)zq=false;
let dxt={stu:dx.value,all:question.value.onechoose}
if(zq){
resulttm.stu=question.value.print
resulttm.all=question.value.print
resulttm.dxt=dxt
}else{
resulttm.stu=0
resulttm.all=question.value.print
resulttm.dxt=dxt
}
console.log(resulttm)
}else if(question.value.tx==='判断题'){
let zq=true;
let pd_work="no"
if(pdt.value==="对")
pd_work="√"
else if (pdt.value==="错")
pd_work="×"
if(pd_work!==question.value.onechoose)zq=false;
let dxt={stu:pd_work,all:question.value.onechoose}
if(zq){
resulttm.stu=question.value.print
resulttm.all=question.value.print
resulttm.dxt=dxt
}else{
resulttm.stu=0
resulttm.all=question.value.print
resulttm.dxt=dxt
}
console.log(resulttm)
}
uni.request({
url: 'http://localhost:3400/apistu/inclassdati_tijiao',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
uuid: uid.value,
kch:'202413501',
kctime:'n8z1A1',
xuehao: '202413501063',
time:moment().format('YYYY-MM-DD HH:mm:ss'),
name:"刘东阳",
touxiangcolor:"red",
tmresult:resulttm
},
success: (res) => { /* ... */
uni.$emit('finish_ex',{uuid:uid.value,wb:'已完成'})
console.log(res.data)
}
});
dati_able.value=false
tijiao_type.value="已提交"
}
onMounted(() => {
startTimer();
});
//
onUnmounted(() => {
stopTimer();
});
</script>
<style scoped>
.controls {
margin-top: 30px;
display: flex;
justify-content: center;
gap: 15px;
/* 控制按钮之间间距 */
flex-wrap: wrap;
}
.controls button {
padding: 12px 34px;
font-size: 16px;
border-radius: 8px;
background-color: #4CAF50;
color: white;
border: none;
transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
cursor: pointer;
}
.controls button:hover {
background-color: #45a049;
transform: translateY(-2px);
}
.controls button:active {
transform: translateY(0);
}
.controls button[type="primary"] {
background-color: #2196F3;
}
.controls button[type="primary"]:hover {
background-color: #1976D2;
}
.question-image{
margin-left: 35px;
}
.grid-container-pd
{
display: flex;
flex-wrap: wrap;
margin-left: 60px;
}
.grid-container {
display: flex;
flex-wrap: wrap;
margin-left: 10vw;
}
.grid-item {
width: calc(25% - 10px); /* 每个项目宽度为25%,减去间距 */
margin: 5px; /* 项目之间的间距 */
box-sizing: border-box; /* 确保padding和border不会影响宽度 */
}
.choose-button{
margin-top: 20px;
font-size: 18px;
margin-left: 10px;
font-weight: bold;
height: 18vw;
width: 18vw;
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
border-radius: 50%;
}
.tkt-input {
border: 1rpx solid black;
border-radius: 8rpx;
padding: 10rpx 20rpx;
margin: 20rpx 0;
width: 60vw;
color: #00f0ff;
display: inline-block;
position: relative;
top: 24px;
left: 10px;
}
.xuhao{
width: 35px;
height: 35px;
border-radius: 50%;
background-color: #4a90e2;
display: inline-block;
}
.xuhao_wb{
display: inline-block;
font-size:25px;
margin-left: 6px;
}
.print-wb{
display: inline-block;
margin-left: 20px;
}
.time-wb{
font-size: 40px;
margin-left: 30vw;
}
</style>

@ -0,0 +1,847 @@
<template>
<view class="quiz-container">
<!-- 顶部栏 -->
<view v-if="!showAnswerCardFlag">
<view class="top-bar">
<view class="question-info">
<text class="question-text" v-if="quizData[activeIndex]">{{ quizData[activeIndex].tname}} ({{ JSON.parse(quizData[activeIndex].information).df }} )</text>
</view>
<button class="answer-card-btn" @click="showAnswerCard"></button>
</view>
<view>
<text>{{ moment.utc(totaltime * 1000).format('hh:mm:ss')}}</text>
</view>
<!-- 动态渲染题目 -->
<view v-for="(question, index) in quizData" :key="question.id" class="question">
<view v-if="activeIndex === index">
<view v-if="question.leixing!=='编程题'">
<!-- 题目文本 -->
<image :src="'http://localhost:89/'+JSON.parse(question.information).tm" mode="aspectFit" class="question-image" />
<text>{{question.leixing}}</text>
<!-- 选项支持图片或文本 -->
<view v-if="question.leixing==='单选题'">
<view v-for="(item_choose, index_choose) in JSON.parse(question.information).xuexiang" :key="index_choose" >
<view class="choose-xx" @click="danchoose(index,zm[index_choose])">
<button
:style="{backgroundColor:question.xuexiang===zm[index_choose]?'blue':'#aaa'}"
class="choose-button"
>
{{zm[index_choose]}}
</button>
<image :src="'http://localhost:89/'+item_choose" class="choose-image" mode="aspectFit" />
</view>
</view>
</view>
<view v-if="question.leixing==='多选题'">
<view v-for="(item_choose, index_choose) in JSON.parse(question.information).xuexiang" :key="index_choose" >
<view class="choose-xx" @click="duochoose(index,zm[index_choose])">
<button
:style="{backgroundColor:question.duoxuexiang.includes(zm[index_choose])?'blue':'#aaa'}"
class="choose-button"
>
{{zm[index_choose]}}
</button>
<image :src="'http://localhost:89/'+item_choose" class="choose-image" mode="aspectFit" />
</view>
</view>
</view>
<view v-if="question.leixing==='填空题'">
<view v-for="(item_choose, index_tkx) in JSON.parse(question.information).daan" :key="index_tkx" >
<view class="xuhao">
<h3 class="xuhao_wb">{{index_tkx+1}}</h3>
</view>
<input :value="question.tk[index_tkx]" @input="val=>{tkt_one(val.detail.value,index_tkx);}" type="text" class="tkt-input" />
<text class="print-wb">{{item_choose.print}}</text>
</view>
</view>
<view v-if="question.leixing==='判断题'">
<view class="grid-container-pd" >
<view key="2" class="grid-item">
<button
:style="{backgroundColor:pdt==='对'?'blue':''}"
class="choose-button1"
@click="pdt_one('对')"
>
</button>
</view>
<view key="index" class="grid-item">
</view>
<view key="index+1" class="grid-item">
<button
:style="{backgroundColor:pdt==='错'?'blue':''}"
class="choose-button1"
@click="pdt_one('错')"
>
×
</button>
</view>
</view>
</view>
<!-- 其他题型类似处理 -->
</view>
</view>
</view>
<!-- 答案反馈 -->
<view v-if="showResult" class="result">
<text v-for="(res, key) in results" :key="key" :style="{ color: res ? 'green' : 'red' }">
{{ key + 1 }} {{ res ? '✅ 正确' : '❌ 错误' }}
</text>
</view>
<view v-if="quizData[activeIndex]">
<view v-if="isNumber(quizData[activeIndex].stuprint)" class="score-and-answer">
<text>本题得分{{ quizData[activeIndex].stuprint }}</text>
<view v-if="quizData[activeIndex].leixing==='多选题'">
<text>我的答案{{quizData[activeIndex].duoxuexiang.sort().join(",") }}</text><br/>
<text>正确答案{{JSON.parse(quizData[activeIndex].information).daan.sort().join(",") }}</text>
</view>
<view v-if="quizData[activeIndex].leixing==='单选题'">
<text>我的答案{{quizData[activeIndex].xuexiang }}</text><br/>
<text>正确答案{{JSON.parse(quizData[activeIndex].information).daan }}</text>
</view>
<view v-if="quizData[activeIndex].leixing==='判断题'">
<text>我的答案{{quizData[activeIndex].xuexiang }}</text><br/>
<text>正确答案{{JSON.parse(quizData[activeIndex].information).daan }}</text>
</view>
<view v-if="quizData[activeIndex].leixing==='填空题'">
<view v-for="(item_choose, index_tkx) in JSON.parse(quizData[activeIndex].information).daan" :key="index_tkx" >
<text>我的答案{{quizData[activeIndex].tk[index_tkx]}}</text>
<text>正确答案{{item_choose.daan }}</text>
</view><br/>
</view>
</view>
</view>
</view>
<!-- 答题卡 --><br/>
<view v-if="showAnswerCardFlag" class="answer-card">
<view class="dtk-top">
<view class="dtk-left">
<text>刘东阳</text>
<text class="stuxuehao">202413501062</text>
</view>
<view class="dtk-right">
<text class="dtk-print-title">作业得分</text>
<text class="dtk-print-stu">100</text>
<text class="dtk-print-all">/100</text>
</view>
</view>
<br/>
<view>题目列表</view>
<view class="tm-list">
<view v-for="(question, index) in quizData" :key="question.id" :class="index===activeIndex?'card-item-choose':(isNumber(question.stuprint)?'card-item-nochoose':'card-item-choose-noprint')" @click="goToQuestion(question.id)">
<view v-if="isNumber(question.stuprint)">
<view v-if="question.stuprint==getprint(question)">
<uni-badge class="uni-badge-left-margin" text="√" absolute="rightTop" :offset="[-16, -4]" size="small" :customStyle="{background: 'green'}">
<view>
<text>{{ index + 1 }} </text>
</view>
</uni-badge>
</view>
<view v-if="question.stuprint!==getprint(question)">
<uni-badge class="uni-badge-left-margin" :text='question.stuprint.toString()' absolute="rightTop" :offset="[-16, -4]" size="small" :customStyle="{background: 'red'}">
<view>
<text>{{ index + 1 }}</text>
</view>
</uni-badge>
</view>
</view>
<view v-if="!isNumber(question.stuprint)" >
<view>
<text>{{ index + 1 }}</text>
</view>
</view>
</view>
</view>
<view>
<button class="return-button" @click="returndati"></button>
</view>
<view :style="{height: '20px'}">
</view>
</view>
</view>
<!-- 得分与正确答案展示 -->
<!-- 底部控制按钮 -->
<view class="bottom-controls">
<button @click="prevQuestion"></button>
<button @click="submitAnswers" type="primary">提交答案</button>
<button @click="nextQuestion"></button>
</view>
</template>
<script setup lang="ts">
import {ref, computed, watch, watchEffect} from 'vue'
import {onLoad} from "@dcloudio/uni-app";
import UniBadge from "@/uni_modules/uni-badge/components/uni-badge/uni-badge.vue";
const zm=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
import moment from "moment/moment";
//
const quizData = ref([])
const uid=ref("0")
//
const intervalId = ref<number | null>(null);
const activeIndex = ref(0)
//
const showResult = ref(false)
// /
const showAnswerCardFlag = ref(false)
const endtime=ref("")
//
const currentQuestion = computed(() => {
return quizData[activeIndex.value] || {}
})
// ID
const userAnswers = ref<Record<number, any>>({})
// true/false
const results = ref<boolean[]>([])
//
function handleRadioChange(id: number, value: string) {
userAnswers.value[id] = value
}
//
function prevQuestion() {
if (activeIndex.value > 0) activeIndex.value--
}
//
function nextQuestion() {
if (activeIndex.value < quizData.length - 1) activeIndex.value++
}
//
function goToQuestion(index: number) {
const id=quizData.value.findIndex(item=>item.id==index)
if(id!==-1) {
activeIndex.value = id
showAnswerCardFlag.value = false //
}
}
watch(quizData,function (newValue, oldValue) {
uni.request({
url: 'http://localhost:3400/api/updatezxdt_ex',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
alltm: newValue,
ksh: uid.value,
xuehao: "202413501062"
},
success: (res) => { /* ... */
console.log(res.data)
}
});
},{deep: true});
//
const totaltime=ref(5)
function getprint(question){
if(question.leixing==='填空题'){
let print =0;
JSON.parse(question.information).daan.map((daan, key) => {
print+=parseInt(daan.print);
})
return print;
}else if(question.leixing==='多选题'){
return parseInt(JSON.parse(question.information).df);
}else if(question.leixing==='单选题'){
return parseInt(JSON.parse(question.information).df);
}else if(question.leixing==='判断题'){
return parseInt(JSON.parse(question.information).df);
}
}
function submitAnswers() {
//
console.log(quizData.value[activeIndex.value])
if(quizData.value[activeIndex.value].leixing==="填空题"){
let print =0;
JSON.parse(quizData.value[activeIndex.value].information).daan.map((daan, key) => {
if(daan.daan===quizData.value[activeIndex.value].tk[key]){
print+=parseInt(daan.print);
}
})
console.log(print)
quizData.value[activeIndex.value].stuprint=print;
}
if(quizData.value[activeIndex.value].leixing==="多选题"){
let print =0;
let zq=true;
JSON.parse(quizData.value[activeIndex.value].information).daan.map((choose_one)=>{
if(!quizData.value[activeIndex.value].duoxuexiang.includes(choose_one)){
zq=false;
}
})
if(quizData.value[activeIndex.value].duoxuexiang.length!==JSON.parse(quizData.value[activeIndex.value].information).daan.length){
zq=false;
}
if(zq){
print=parseInt(JSON.parse(quizData.value[activeIndex.value].information).df)
}
console.log(print)
quizData.value[activeIndex.value].stuprint= print;
}else if(quizData.value[activeIndex.value].leixing==="单选题"){
if(quizData.value[activeIndex.value].xuexiang===JSON.parse(quizData.value[activeIndex.value].information).daan){
quizData.value[activeIndex.value].stuprint=parseInt(JSON.parse(quizData.value[activeIndex.value].information).df)
}else {
quizData.value[activeIndex.value].stuprint=0
}
}else if(quizData.value[activeIndex.value].leixing==="判断题"){
if(quizData.value[activeIndex.value].xuexiang===JSON.parse(quizData.value[activeIndex.value].information).daan){
quizData.value[activeIndex.value].stuprint=JSON.parse(quizData.value[activeIndex.value].information).df
}else {
quizData.value[activeIndex.value].stuprint=0
}
}
//////
if(1===3) {
results.value[activeIndex.value] = false
const currentQuestion = quizData[activeIndex.value]
const userAnswer = userAnswers.value[currentQuestion.id] ?? ''
const correctAnswer = String(currentQuestion.answer)
const normalizedUserAnswer = String(userAnswer).trim().toUpperCase()
const normalizedCorrectAnswer = correctAnswer.trim().toUpperCase()
const isCorrect = normalizedUserAnswer == normalizedCorrectAnswer
results.value[activeIndex.value] = isCorrect
showResult.value = true
}
}
//
const score = computed(() => {
const currentResult = results.value[activeIndex.value]
return currentResult ? currentQuestion.value.score : 0
})
//
const correctAnswer = computed(() => {
const currentQuestion = quizData[activeIndex.value]
if (!currentQuestion) return ''
const correctOption = currentQuestion.options.find(
option => option.value === currentQuestion.answer
)
return correctOption ? correctOption.text : ''
})
function pdt_one(xx:string){
quizData.value[activeIndex.value].xuexiang = xx
}
const startTimer = () => {
intervalId.value = setInterval(() => {
if (totaltime.value>0) {
totaltime.value=moment(endtime.value).diff(moment(),'seconds')
} else {
stopTimer();
}
}, 1000);
};
//
const stopTimer = () => {
if (intervalId.value !== null) {
clearInterval(intervalId.value);
intervalId.value = null;
}
};
function danchoose(index: number,choose:string) {
console.log(choose);
console.log(index)
quizData.value[index].xuexiang = choose
}
function duochoose(index: number,choose:string) {
console.log(choose);
console.log(index)
if(quizData.value[index].duoxuexiang.includes(choose)) {
quizData.value[index].duoxuexiang = quizData.value[index].duoxuexiang.filter(item => item !== choose);
}else {
quizData.value[index].duoxuexiang.push(choose);
}
}
//
function showAnswerCard() {
showAnswerCardFlag.value = true
}
function isNumber(value) {
return typeof value === 'number';
}
function tkt_one(xx:string,index:number){
quizData.value[activeIndex.value].tk[index]=xx;
}
onLoad((options) => {
console.log(options)
uid.value=options.uuid;
uni.request({
url: 'http://localhost:3400/api/getptatmj',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
ksh:options.uuid,
xuehao:"202413501062"
},
success: (res) => { /* ... */
console.log(res.data)
console.log(JSON.parse(res.data.tmlist))
quizData.value=JSON.parse(res.data.tmlist)
console.log(quizData.value)
console.log(1223)
const seconds=JSON.parse(res.data.information).seconds;
let end=moment(res.data.stustart)
end.add(seconds,'seconds');
endtime.value=end.format('YYYY-MM-DD HH:mm:ss');
console.log(end.format('YYYY-MM-DD HH:mm:ss'))
startTimer();
}
});
})
function returndati(){
showAnswerCardFlag.value = false
}
</script>
<style scoped>
.quiz-container {
padding: 5px;
position: relative;
}
/* 顶部栏样式 */
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 3px;
border-bottom: 1px solid #e0e0e0;
background-color: #d97e7e;
}
.top-bar1 {
display: inline;
justify-content: space-between;
align-items: center;
padding: 3px;
border-bottom: 1px solid #e0e0e0;
background-color: #d97e7e;
width: 100vw;
}
.question-text1 {
font-size: 16px;
color: #333;
font-weight: bold;
margin-left: 10px;
}
.answer-card-btn1 {
background-color: #63a2cf;
border: 1px solid #a8d5ff;
color: #000;
padding: 3px 6px;
border-radius: 4px;
font-size: 12px;
margin-left: auto;
}
/* 新增左侧竖线装饰 */
.question-info1::before {
content: '';
display: inline-block;
width: 4px;
height: 20px;
background-color: #007bff;
margin-right: 3px;
}
.question-text {
font-size: 16px;
color: #333;
font-weight: bold;
margin-left: 10px;
}
.answer-card-btn {
background-color: #63a2cf;
border: 1px solid #a8d5ff;
color: #000;
padding: 3px 6px;
border-radius: 4px;
font-size: 12px;
background-color: #4490ca;
margin-left: auto;
}
/* 新增左侧竖线装饰 */
.question-info::before {
content: '';
display: inline-block;
width: 4px;
height: 20px;
background-color: #007bff;
margin-right: 3px;
}
.question {
margin-bottom: 20px;
}
.question-text {
font-size: 16px;
margin-bottom: 10px;
}
.option {
display: flex;
align-items: center;
margin: 5px 0;
}
.option-image {
width: 50px;
height: 50px;
margin-left: 10px;
}
.result {
margin-top: 20px;
}
.answer-card {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 20px;
flex-direction: column;
}
.card-item-choose {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
width: 20px;
height: 20px;
background-color: #0ef306;
margin-left: 20px;
}
.card-item-nochoose {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
width: 20px;
height: 20px;
background-color: #fcffea;
margin-left: 20px;
}
.card-item-choose-noprint {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
width: 20px;
height: 20px;
background-color: #fbfbfb;
}
.score-and-answer {
margin-top: 20px;
background-color: #f0f0f0;
padding: 10px;
border-radius: 8px;
}
.bottom-controls {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 5px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.1);
}
.bottom-controls button {
padding: 5px 10px;
font-size: 14px;
border-radius: 4px;
background-color: #f0f0f0;
color: #333;
border: none;
transition: all 0.3s ease;
}
.bottom-controls button:hover {
background-color: #e0e0e0;
}
.bottom-controls button[type="primary"] {
background-color: #2196f3;
color: #fff;
}
.question-image{
margin-left: 35px;
}
.bottom-controls button[type="primary"]:hover {
background-color: #1976d2;
}
.choose-button{
font-size: 18px;
font-weight: bold;
height: 14vw;
width: 14vw;
display: inline-block;
border-radius: 50%;
}
.choose-image{
height: 12vh;
display: inline-block;
max-width: 70vw;
position: absolute;
left: 0;
top: -2vh;
}
.choose-xx{
position:relative;
margin-bottom: 20px;
}
.tkt-input {
border: 1rpx solid black;
border-radius: 8rpx;
padding: 10rpx 20rpx;
margin: 20rpx 0;
width: 60vw;
color: #00f0ff;
display: inline-block;
position: relative;
top: 24px;
left: 10px;
}
.xuhao{
width: 35px;
height: 35px;
border-radius: 50%;
background-color: #4a90e2;
display: inline-block;
}
.xuhao_wb{
display: inline-block;
font-size:25px;
margin-left: 6px;
}
.print-wb{
display: inline-block;
margin-left: 20px;
}
.grid-container-pd
{
display: flex;
flex-wrap: wrap;
margin-left: 60px;
}
.grid-container {
display: flex;
flex-wrap: wrap;
margin-left: 10vw;
}
.grid-item {
width: calc(25% - 10px); /* 每个项目宽度为25%,减去间距 */
margin: 5px; /* 项目之间的间距 */
box-sizing: border-box; /* 确保padding和border不会影响宽度 */
}
.choose-button1{
margin-top: 20px;
font-size: 18px;
margin-left: 10px;
font-weight: bold;
height: 18vw;
width: 18vw;
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
border-radius: 50%;
}
.container1 {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.user-info {
display: flex;
align-items: center;
padding: 30rpx;
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 30rpx;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin-right: 30rpx;
}
.user-text {
display: flex;
flex-direction: column;
}
.username {
font-size: 36rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.user-id {
font-size: 28rpx;
color: #999;
}
.score-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
}
.card-header {
margin-bottom: 30rpx;
}
.card-title {
font-size: 32rpx;
font-weight: bold;
display: block;
margin-bottom: 10rpx;
}
.card-subtitle {
font-size: 26rpx;
color: #999;
}
.score-content {
display: flex;
justify-content: space-between;
}
.score-item {
flex: 1;
text-align: center;
}
.score-label {
font-size: 28rpx;
color: #666;
display: block;
margin-bottom: 10rpx;
}
.score-value {
font-size: 40rpx;
font-weight: bold;
color: #07c160;
}
.dtk-left{
display:flex ;
flex-direction:column;
}
.dtk-right {
display:flex;
position: absolute; /* 绝对定位 */
right: 5px; /* 右对齐 */
align-items: end; /* 使子元素垂直对齐到容器的底部 */
}
.dtk-top {
display: flex;
width: 100vw;
}
.dtk-print-stu{
font-size: 25px;
}
.dtk-print-all{
font-size: 11px;
position: relative;
top: 12px;
}
.dtk-print-title{
font-size: 11px;
position: relative;
top: 12px;
margin-right: 20px;
}
.stuname{
}
.stuxuehao{
font-size: 10px;
}
.tm-list{
display: flex;
}
.return-button{
width: 80vw;
background-color: #ffffff;
color: green;
border: 3px solid green;
}
</style>

@ -0,0 +1,214 @@
<template>
<div class="question-list">
<ul>
<li
v-for="question in questions"
:key="question.uuid"
:class="['question-item', { selected: question.selected }]"
@click="handleSelect(question.id)"
>
<div class="info">
<span class="title">{{ question.name }}</span>
<div class="meta">
<span class="teacher">教师{{ question.teaname}}</span>
<span class="time">时间{{ moment(question.starttime).format('YYYY-MM-DD HH:mm:ss') }} - {{ moment(question.endtime).format('YYYY-MM-DD HH:mm:ss') }}</span>
</div>
</div>
<div class="stats">
<span class="num">{{question.tmlist? JSON.parse(question.tmlist).length:0 }}</span>
<span class="total">总分{{ question.print }}</span>
</div>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import {onLoad} from "@dcloudio/uni-app";
import moment from "moment";
//
interface Question {
uuid: string
title: string
course: string
teacher: string
startTime: string
endTime: string
num: number
total: number
selected: boolean
}
//
const questions = ref([
{
uuid: '1',
title: '题目1',
course: '课程1',
teacher: '教师1',
startTime: '2023-10-01T08:00:00',
endTime: '2023-10-01T09:00:00',
num: 1,
total: 100,
selected: false
},
{
uuid: '2',
title: '题目2',
course: '课程2',
teacher: '教师2',
startTime: '2023-10-02T08:00:00',
endTime: '2023-10-02T09:00:00',
num: 2,
total: 200,
selected: false
}
])
onLoad((options)=>{
uni.request({
url: 'http://localhost:3400/api/timujistunew_ex',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
xuehao:'202413501062'
},
success: (res) => { /* ... */
console.log(res.data)
questions.value=res.data
}
});
})
//
const handleSelect = (uuid) => {
console.log(uuid)
uni.navigateTo({
url: '/pages/quiz_outclass/quiz_outclass?uuid='+uuid
});
}
//
const formatTime = (time: string): string => {
const date = new Date(time)
return `${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`
}
</script>
<style scoped>
.question-list {
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
min-height: 100vh;
padding: 40px 20px;
color: #fff;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.question-list h2 {
text-align: center;
font-size: 2rem;
margin-bottom: 20px;
color: #00ffff;
text-shadow: 0 0 10px #00ffff;
}
ul {
list-style-type: none;
padding: 0;
max-width: 900px;
margin: 0 auto;
}
.question-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
margin-bottom: 16px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
cursor: pointer;
transition: all 0.3s ease-in-out;
border-left: 4px solid transparent;
}
.question-item:hover {
transform: scale(1.02);
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 24px rgba(0, 255, 255, 0.2);
}
.question-item.selected {
background-color: rgba(0, 255, 255, 0.2);
border-left-color: #00ffff;
box-shadow: 0 0 10px #00ffff;
}
.info {
display: flex;
flex-direction: column;
}
.title {
font-size: 18px;
font-weight: bold;
color: #ffffff;
margin-bottom: 6px;
}
.meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
font-size: 12px;
color: #ccc;
}
.meta span {
padding: 4px 8px;
border-radius: 6px;
font-weight: 500;
}
.course {
background-color: rgba(0, 255, 255, 0.2);
color: #00ffff;
}
.teacher {
background-color: rgba(255, 255, 0, 0.2);
color: #fffa86;
}
.time {
background-color: rgba(255, 255, 255, 0.15);
color: #ddd;
width: 60vw;
}
.stats {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.num {
font-size: 12px;
background-color: rgba(0, 255, 127, 0.2);
color: #00ff7f;
padding: 4px 8px;
border-radius: 6px;
}
.total {
font-size: 12px;
background-color: rgba(255, 105, 180, 0.2);
color: #ff69b4;
padding: 4px 8px;
border-radius: 6px;
margin-top: 6px;
}
</style>

@ -0,0 +1,45 @@
<template>
<view class="role-select-page">
<view class="title">请选择您的身份</view>
<view class="role-buttons">
<button @click="selectStudent"></button>
<button @click="selectTeacher"></button>
</view>
</view>
</template>
<script setup>
import { uni } from '@dcloudio/uni-app';
const selectStudent = () => {
uni.reLaunch({
url: '/pages/Student/learning/learning'
});
};
const selectTeacher = () => {
uni.reLaunch({
url: '/pages/Teacher/teacherCourseManagement/teacherCourseManagement'
});
};
</script>
<style scoped>
.role-select-page {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
}
.title {
font-size: 24px;
margin-bottom: 20px;
}
.role-buttons {
display: flex;
gap: 20px;
}
</style>

@ -0,0 +1,433 @@
<template>
<view class="cyber-container">
<!-- 霓虹风格顶部栏 -->
<view class="cyber-header">
<view class="header-glitch" data-text="">智能课程表</view>
<view class="week-display">
<text class="week-label">当前周次</text>
<text class="week-value">{{ currentWeek }}</text>
</view>
</view>
<!-- 全息投影风格课表 -->
<view class="hologram-grid">
<!-- 星期表头 -->
<view class="grid-header">
<view class="time-slot">时间</view>
<view
v-for="day in weekDays"
:key="day.value"
class="day-header"
:class="{ today: isToday(day.value) }"
>
{{ day.label }}
</view>
</view>
<!-- 课程行 -->
<view
v-for="(timeSlot, slotIndex) in timeSlots"
:key="slotIndex"
class="grid-row"
>
<view class="time-slot">
<text class="time">{{ timeSlot }}</text>
<text class="slot-num">{{ slotIndex + 1 }}</text>
</view>
<!-- <view>测试{{ coursesData.mon[0].name }}</view> -->
<view
v-for="day in weekDays"
:key="day.value"
class="course-slot"
@click="showCourseDetail(day.value, slotIndex)"
>
<!-- <course-hologram
v-if="hasCourse(day.value, slotIndex)"
:course="getCourse(day.value, slotIndex)"
:active="isCurrentWeekCourse(day.value, slotIndex)"
/> -->
<course-hologram
v-if="hasCourse(day.value, slotIndex)"
:course="getCourse(day.value, slotIndex)"
:active="isCurrentWeekCourse(day.value, slotIndex)"
/>
</view>
</view>
</view>
<!-- 全息弹窗 -->
<uni-popup ref="popup" type="dialog">
<uni-popup-dialog
v-if="currentCourse"
:title="currentCourse.name"
:message="courseDetail"
confirmText="确定"
@confirm="closePopup"
/>
</uni-popup>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import dayjs from 'dayjs'
import 'dayjs/locale/zh-cn'
// dayjs
dayjs.locale('zh-cn')
//
const COLOR_SCHEME = {
math: '#00ff9d',
programming: '#00b8ff',
english: '#ff2d75',
physics: '#ff9d00',
default: '#8a2be2'
}
//
const weekDays = [
{ label: '周一', value: 'mon' },
{ label: '周二', value: 'tue' },
{ label: '周三', value: 'wed' },
{ label: '周四', value: 'thu' },
{ label: '周五', value: 'fri' },
{ label: '周六', value: 'sat' },
{ label: '周日', value: 'sun' }
]
//
const timeSlots = [
'08:20-09:05', '09:10-09:55', '10:15-11:00',
'11:05-11:50', '14:00-14:45', '14:50-15:35',
'15:55-16:40', '16:45-17:30', '18:30-19:15',
'19:20-20:05', '20:10-20:55', '21:00-21:45'
]
//
const coursesData = {
mon: [
{
section: 0,
name: '高等数学A(一)',
teacher: '李教授',
location: 'A101',
weeks: '1-16',
type: 'math'
},
{
section: 1,
name: '高等数学A(一)',
teacher: '李教授',
location: 'A101',
weeks: '1-19',
type: 'math'
}
],
tue: [
{
section: 3,
name: '程序设计基础',
teacher: '王老师',
location: '计科实验室',
weeks: '1-12',
type: 'programming'
}
],
wed: [
{
section: 2,
name: '大学英语',
teacher: '张老师',
location: 'C203',
weeks: '1-18',
type: 'english'
}
]
}
//
const currentWeek = ref(1)
const currentCourse = ref(null)
const popup = ref(null)
//
const courseDetail = computed(() => {
if (!currentCourse.value) return ''
return `教师: ${currentCourse.value.teacher}\n地点: ${currentCourse.value.location}\n周次: ${currentCourse.value.weeks}`
})
//
const calculateCurrentWeek = () => {
const startDate = dayjs('2025-03-1') //
const weeks = dayjs().diff(startDate, 'week') + 1
currentWeek.value = Math.max(1, Math.min(weeks, 18)) // 1-18
}
//
const isToday = (dayValue) => {
const dayMap = { mon: 1, tue: 2, wed: 3, thu: 4, fri: 5, sat: 6, sun: 0 }
return dayMap[dayValue] === dayjs().day()
}
//
const hasCourse = (day, section) => {
return coursesData[day]?.some(c => c.section === section) ?? false
}
//
const getCourse = (day, section) => {
return coursesData[day]?.find(c => c.section === section) || null
}
//
const isCurrentWeekCourse = (day, section) => {
const course = getCourse(day, section)
if (!course) return false
const weeks = course.weeks
if (weeks.includes('-')) {
const [start, end] = weeks.split('-').map(Number)
return currentWeek.value >= start && currentWeek.value <= end
} else if (weeks.includes(',')) {
return weeks.split(',').map(Number).includes(currentWeek.value)
}
return Number(weeks) === currentWeek.value
}
//
const showCourseDetail = (day, section) => {
currentCourse.value = getCourse(day, section)
if (currentCourse.value) {
popup.value.open()
}
}
//
const closePopup = () => {
popup.value.close()
}
//
onMounted(() => {
calculateCurrentWeek()
})
//
const CourseHologram = {
props: ['course', 'active'],
computed: {
courseColor() {
return COLOR_SCHEME[this.course.type] || COLOR_SCHEME.default
}
},
template: `
<view
class="hologram-card"
:style="{
'--hologram-color': courseColor,
opacity: active ? 1 : 0.6
}"
>
<text class="course-name">{{ course.name }}</text>
<text class="course-location">{{ course.location }}</text>
<view class="hologram-effect"></view>
</view>
`
}
</script>
<style lang="scss">
.cyber-container {
background: #0a0e17;
color: #00ff9d;
min-height: 100vh;
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
}
.cyber-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 30rpx;
background: rgba(0, 20, 40, 0.8);
border-bottom: 1px solid rgba(0, 255, 157, 0.3);
.header-glitch {
position: relative;
font-size: 44rpx;
font-weight: bold;
color: #00ff9d;
text-shadow: 0 0 10px rgba(0, 255, 157, 0.5);
}
.week-display {
display: flex;
align-items: center;
.week-label {
font-size: 28rpx;
margin-right: 10rpx;
color: rgba(0, 255, 157, 0.7);
}
.week-value {
font-size: 36rpx;
font-weight: bold;
color: #00ff9d;
}
}
}
.hologram-grid {
margin: 20rpx;
border: 1px solid rgba(0, 255, 157, 0.2);
border-radius: 8rpx;
overflow: hidden;
.grid-header {
display: flex;
background: rgba(0, 30, 60, 0.8);
.time-slot {
width: 160rpx;
padding: 20rpx;
text-align: center;
border-right: 1px solid rgba(0, 255, 157, 0.2);
font-size: 28rpx;
}
.day-header {
flex: 1;
padding: 20rpx;
text-align: center;
border-right: 1px solid rgba(0, 255, 157, 0.2);
font-size: 28rpx;
&:last-child {
border-right: none;
}
&.today {
color: #00ff9d;
position: relative;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: 10rpx;
left: 50%;
transform: translateX(-50%);
width: 40rpx;
height: 3rpx;
background: #00ff9d;
}
}
}
}
.grid-row {
display: flex;
border-bottom: 1px solid rgba(0, 255, 157, 0.1);
&:last-child {
border-bottom: none;
}
.time-slot {
width: 160rpx;
padding: 15rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-right: 1px solid rgba(0, 255, 157, 0.1);
font-size: 24rpx;
color: #00b8ff;
.slot-num {
font-size: 20rpx;
color: rgba(0, 184, 255, 0.6);
}
}
.course-slot {
flex: 1;
min-height: 120rpx;
padding: 10rpx;
border-right: 1px solid rgba(0, 255, 157, 0.1);
&:last-child {
border-right: none;
}
}
}
}
.hologram-card {
position: relative;
height: 100%;
padding: 15rpx;
border-radius: 6rpx;
background: rgba(var(--hologram-color-rgb), 0.1);
border: 1px solid var(--hologram-color);
color: white;
overflow: hidden;
.course-name {
font-size: 26rpx;
font-weight: bold;
margin-bottom: 5rpx;
display: block;
}
.course-location {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.8);
}
.hologram-effect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
135deg,
rgba(var(--hologram-color-rgb), 0.1) 0%,
rgba(var(--hologram-color-rgb), 0.3) 50%,
rgba(var(--hologram-color-rgb), 0.1) 100%
);
opacity: 0.5;
z-index: -1;
}
}
/* 颜色变量转换 */
.hologram-card {
--hologram-color-rgb: 0, 255, 157;
}
.hologram-card[style*="math"] {
--hologram-color-rgb: 0, 255, 157;
}
.hologram-card[style*="programming"] {
--hologram-color-rgb: 0, 184, 255;
}
.hologram-card[style*="english"] {
--hologram-color-rgb: 255, 45, 117;
}
.hologram-card[style*="physics"] {
--hologram-color-rgb: 255, 157, 0;
}
.hologram-card[style*="default"] {
--hologram-color-rgb: 138, 43, 226;
}
</style>

@ -0,0 +1,26 @@
<template>
<view class="container">
<text class="title">成绩</text>
<!-- 页面内容 -->
</view>
</template>
<script setup>
import {
ref
} from 'vue'
//
const pageTitle = ref('成绩')
</script>
<style>
.container {
padding: 20px;
}
.title {
font-size: 18px;
color: #333;
}
</style>

@ -0,0 +1,26 @@
<template>
<view class="container">
<text class="title">设置</text>
<!-- 页面内容 -->
</view>
</template>
<script setup>
import {
ref
} from 'vue'
//
const pageTitle = ref('设置')
</script>
<style>
.container {
padding: 20px;
}
.title {
font-size: 18px;
color: #333;
}
</style>

@ -0,0 +1,376 @@
<template>
<view class="tech-container">
<!-- 科技风背景元素 -->
<view class="tech-bg">
<view class="tech-grid"></view>
<view class="tech-circle circle-1"></view>
<view class="tech-circle circle-2"></view>
</view>
<!-- 用户信息卡片 -->
<view class="tech-card user-card">
<view class="user-info">
<view class="avatar-wrapper" @click="chooseAvatar">
<image class="avatar" :src="userInfo.avatar || '/static/user/avatar.png'" mode="aspectFill"></image>
<view class="avatar-border"></view>
</view>
<view class="info-right">
<view class="name-line" @click="editName">
<text class="username">{{ userInfo.name || '张三' }}</text>
<uni-icons type="compose" size="18" color="#00f0ff"></uni-icons>
</view>
<view class="tech-input-line" @click="editStudentId">
<text class="tech-label">学号:</text>
<text class="student-id">{{ userInfo.studentId || '20230001' }}</text>
<uni-icons type="arrowright" size="18" color="#7f8fa6"></uni-icons>
</view>
</view>
</view>
<view class="tech-divider"></view>
</view>
<!-- 功能卡片 -->
<view class="tech-card function-card">
<view class="function-grid">
<view class="function-item" v-for="item in functionList" :key="item.type" @click="navigateTo(item.type)">
<view class="tech-icon-wrapper">
<image class="function-icon" :src="item.icon"></image>
<view class="icon-halo"></view>
</view>
<text class="function-text">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
//
const userInfo = ref({
name: '张三',
studentId: '20230001',
avatar: ''
})
//
const functionList = ref([
{ type: 'leave', name: '请假', icon: '/static/user/leave.png' },
{ type: 'plan', name: '培养方案', icon: '/static/user/plan.png' },
{ type: 'course', name: '选课', icon: '/static/user/course.png' },
{ type: 'score', name: '成绩', icon: '/static/user/score.png' },
{ type: 'settings', name: '设置', icon: '/static/user/settings.png' }
])
//
const navigateTo = (type) => {
const pages = {
'leave': '/pages/leave/leave',
'plan': '/pages/plan/plan',
'course': '/pages/course/course',
'score': '/pages/score/score',
'settings': '/pages/settings/settings'
}
uni.navigateTo({ url: pages[type] })
}
//
const chooseAvatar = () => {
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
userInfo.value.avatar = res.tempFilePaths[0]
saveUserInfo()
uni.showToast({ title: '头像更新成功', icon: 'success' })
},
fail: () => {
uni.showToast({ title: '取消选择', icon: 'none' })
}
})
}
//
const editName = () => {
uni.showModal({
title: '修改姓名',
content: userInfo.value.name,
editable: true,
placeholderText: '请输入姓名',
confirmText: '保存',
cancelText: '取消',
success: (res) => {
if (res.confirm && res.content) {
userInfo.value.name = res.content.trim()
saveUserInfo()
uni.showToast({ title: '姓名修改成功', icon: 'success' })
}
}
})
}
//
const editStudentId = () => {
uni.showModal({
title: '修改学号',
content: userInfo.value.studentId,
editable: true,
placeholderText: '请输入学号',
confirmText: '保存',
cancelText: '取消',
success: (res) => {
if (res.confirm && res.content) {
userInfo.value.studentId = res.content.trim()
saveUserInfo()
uni.showToast({ title: '学号修改成功', icon: 'success' })
}
}
})
}
//
const saveUserInfo = () => {
uni.setStorageSync('userInfo', userInfo.value)
}
//
onLoad(() => {
const cachedUserInfo = uni.getStorageSync('userInfo')
if (cachedUserInfo) {
userInfo.value = cachedUserInfo
}
})
</script>
<style lang="scss" scoped>
.tech-container {
padding: 30rpx;
background: linear-gradient(135deg, #0a0e21 0%, #121a3a 100%);
min-height: 100vh;
position: relative;
overflow: hidden;
}
/* 科技风背景 */
.tech-bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
overflow: hidden;
.tech-grid {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
linear-gradient(rgba(0, 240, 255, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 240, 255, 0.05) 1px, transparent 1px);
background-size: 40rpx 40rpx;
}
.tech-circle {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle, rgba(0, 240, 255, 0.05), transparent 70%);
&.circle-1 {
width: 400rpx;
height: 400rpx;
top: -200rpx;
right: -200rpx;
}
&.circle-2 {
width: 300rpx;
height: 300rpx;
bottom: -150rpx;
left: -150rpx;
}
}
}
.tech-card {
background: rgba(30, 41, 59, 0.6);
border-radius: 20rpx;
margin-bottom: 30rpx;
overflow: hidden;
position: relative;
backdrop-filter: blur(10px);
border: 1rpx solid rgba(0, 240, 255, 0.1);
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
&::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2rpx;
background: linear-gradient(90deg, transparent, #00f0ff, transparent);
}
}
.user-card {
padding: 40rpx;
.user-info {
display: flex;
align-items: center;
.avatar-wrapper {
position: relative;
width: 140rpx;
height: 140rpx;
margin-right: 30rpx;
.avatar {
width: 100%;
height: 100%;
border-radius: 50%;
}
.avatar-border {
position: absolute;
top: -4rpx;
left: -4rpx;
right: -4rpx;
bottom: -4rpx;
border-radius: 50%;
border: 2rpx solid #00f0ff;
animation: pulse 2s infinite;
}
}
.info-right {
flex: 1;
.name-line {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.username {
font-size: 40rpx;
font-weight: bold;
color: #fff;
margin-right: 20rpx;
background: linear-gradient(to right, #00f0ff, #0088ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.tech-input-line {
display: flex;
align-items: center;
padding: 16rpx 0;
.tech-label {
font-size: 30rpx;
color: #7f8fa6;
margin-right: 16rpx;
}
.student-id {
font-size: 30rpx;
color: #fff;
flex: 1;
}
}
}
}
.tech-divider {
height: 2rpx;
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.3), transparent);
margin-top: 20rpx;
}
}
.function-card {
padding: 30rpx;
.function-grid {
display: flex;
flex-direction: column;
.function-item {
display: flex;
align-items: center;
padding: 30rpx 0;
position: relative;
&:not(:last-child)::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1rpx;
background: linear-gradient(90deg, transparent, rgba(0, 240, 255, 0.1), transparent);
}
.tech-icon-wrapper {
width: 90rpx;
height: 90rpx;
margin-right: 30rpx;
background: rgba(0, 240, 255, 0.1);
border-radius: 22rpx;
display: flex;
align-items: center;
justify-content: center;
position: relative;
border: 1rpx solid rgba(0, 240, 255, 0.2);
.function-icon {
width: 50rpx;
height: 50rpx;
z-index: 1;
}
.icon-halo {
position: absolute;
width: 100%;
height: 100%;
border-radius: 22rpx;
background: radial-gradient(circle, rgba(0, 240, 255, 0.3), transparent 70%);
opacity: 0;
transition: opacity 0.3s;
}
}
.function-text {
font-size: 32rpx;
color: #fff;
flex: 1;
}
&:active {
.tech-icon-wrapper .icon-halo {
opacity: 1;
}
}
}
}
}
/* 动画 */
@keyframes pulse {
0%, 100% {
opacity: 0.7;
transform: scale(0.98);
}
50% {
opacity: 1;
transform: scale(1.02);
}
}
</style>

@ -0,0 +1,949 @@
<template>
<view class="quiz-container">
<!-- 顶部栏 -->
<view >
<view class="top-bar">
<view class="question-info">
<text class="question-text" v-if="quizData[activeIndex]">{{ }} ({{ }} )</text>
</view>
<button class="answer-card-btn" @click="showAnswerCard"></button>
</view>
<view>
<text>{{ moment.utc(totaltime * 1000).format('hh:mm:ss')}}</text>
</view>
<!-- 动态渲染题目 -->
<view v-for="(question, index) in quizData" :key="question.uuid" class="question">
<view>
<view>
<!-- 题目文本 -->
<view>
<image v-if="question.tm.lx==='pic'" :src="question.tm.pic" mode="aspectFit" class="question-image" />
<text v-if="question.tm.lx==='wb'">{{ question.tm.wb}}</text>
</view>
<text>{{question.leixing}}</text>
<!-- 选项支持图片或文本 -->
<view v-if="question.lx==='单选题'">
<view v-for="(item_choose, index_choose) in question.xuexiang" :key="index_choose" >
<view class="choose-xx" @click="danchoose(index,zm[index_choose])" >
<button
:style="{backgroundColor:question.daan===zm[index_choose]?'blue':'#aaa'}"
class="choose-button"
>
{{zm[index_choose]}}
</button>
<view class="choose-wborpic" >
<image v-if="item_choose.lx==='pic'" :src="item_choose.pic" class="choose-image" mode="aspectFit" />
<text v-if="item_choose.lx==='wb'" >{{item_choose.wb}}</text>
</view>
</view>
</view>
</view>
<view v-if="question.lx==='简答题'" class="duoxuanti_all">
<button type="button" @click="scpic"></button>
<text>12345</text>
</view>
<view v-if="question.lx==='多选题'" class="duoxuanti_all">
<view v-for="(item_choose, index_choose) in question.xuexiang" :key="index_choose" >
<view class="choose-xx" @click="duochoose(index,zm[index_choose])">
<button
:style="{backgroundColor:question.daan.includes(zm[index_choose])?'blue':'#aaa'}"
class="choose-button"
>
{{zm[index_choose]}}
</button>
<view class="choose-wborpic">
<image v-if="item_choose.lx==='pic'" :src="item_choose.pic" class="choose-image" mode="aspectFit" />
<text v-if="item_choose.lx==='wb'">{{item_choose.wb}}</text>
</view>
</view>
</view>
</view>
<view v-if="question.lx==='填空题'">
<view >
<input :value="question.sr" @input="val=>{tkt_one(val.detail.value,index);}" type="text" class="tkt-input" />
</view>
</view>
<view v-if="question.lx==='判断题'">
<view class="grid-container-pd" >
<view key="2" class="grid-item">
<button
:style="{backgroundColor:question.daan==='对'?'blue':''}"
class="choose-button1"
@click="pdt_one('对',index)"
>
</button>
</view>
<view key="index" class="grid-item">
</view>
<view key="index+1" class="grid-item">
<button
:style="{backgroundColor:question.daan==='错'?'blue':''}"
class="choose-button1"
@click="pdt_one('错',index)"
>
×
</button>
</view>
</view>
</view>
<!-- 其他题型类似处理 -->
</view>
</view>
</view>
<!-- 答案反馈 -->
<view v-if="showResult" class="result">
<text v-for="(res, key) in results" :key="key" :style="{ color: res ? 'green' : 'red' }">
{{ key + 1 }} {{ res ? '✅ 正确' : '❌ 错误' }}
</text>
</view>
<view v-if="quizData[activeIndex]">
<view v-if="isNumber(quizData[activeIndex].stuprint)" class="score-and-answer">
<text>本题得分{{ quizData[activeIndex].stuprint }}</text>
<view v-if="quizData[activeIndex].leixing==='多选题'">
<text>我的答案{{quizData[activeIndex].duoxuexiang.sort().join(",") }}</text><br/>
<text>正确答案{{JSON.parse(quizData[activeIndex].information).daan.sort().join(",") }}</text>
</view>
<view v-if="quizData[activeIndex].leixing==='单选题'">
<text>我的答案{{quizData[activeIndex].xuexiang }}</text><br/>
<text>正确答案{{JSON.parse(quizData[activeIndex].information).daan }}</text>
</view>
<view v-if="quizData[activeIndex].leixing==='判断题'">
<text>我的答案{{quizData[activeIndex].xuexiang }}</text><br/>
<text>正确答案{{JSON.parse(quizData[activeIndex].information).daan }}</text>
</view>
<view v-if="quizData[activeIndex].leixing==='填空题'">
<view v-for="(item_choose, index_tkx) in JSON.parse(quizData[activeIndex].information).daan" :key="index_tkx" >
<text>我的答案{{quizData[activeIndex].tk[index_tkx]}}</text>
<text>正确答案{{item_choose.daan }}</text>
</view><br/>
</view>
</view>
</view>
</view>
<!-- 答题卡 --><br/>
<view v-if="showAnswerCardFlag" class="answer-card">
<view class="dtk-top">
<view class="dtk-left">
<text>刘东阳</text>
<text class="stuxuehao">202413501062</text>
</view>
<view class="dtk-right">
<text class="dtk-print-title">作业得分</text>
<text class="dtk-print-stu">100</text>
<text class="dtk-print-all">/100</text>
</view>
</view>
<br/>
<view>题目列表</view>
<view class="tm-list">
<view v-for="(question, index) in quizData" :key="question.id" :class="index===activeIndex?'card-item-choose':(isNumber(question.stuprint)?'card-item-nochoose':'card-item-choose-noprint')" @click="goToQuestion(question.id)">
<view v-if="isNumber(question.stuprint)">
<view v-if="question.stuprint==getprint(question)">
<uni-badge class="uni-badge-left-margin" text="√" absolute="rightTop" :offset="[-16, -4]" size="small" :customStyle="{background: 'green'}">
<view>
<text>{{ index + 1 }} </text>
</view>
</uni-badge>
</view>
<view v-if="question.stuprint!==getprint(question)">
<uni-badge class="uni-badge-left-margin" :text='question.stuprint.toString()' absolute="rightTop" :offset="[-16, -4]" size="small" :customStyle="{background: 'red'}">
<view>
<text>{{ index + 1 }}</text>
</view>
</uni-badge>
</view>
</view>
<view v-if="!isNumber(question.stuprint)" >
<view>
<text>{{ index + 1 }}</text>
</view>
</view>
</view>
</view>
<view>
<button class="return-button" @click="returndati"></button>
</view>
<view :style="{height: '20px'}">
</view>
</view>
</view>
<!-- 得分与正确答案展示 -->
<!-- 底部控制按钮 -->
<view class="bottom-controls">
<button @click="prevQuestion"></button>
<button @click="submitAnswers" type="primary">提交答案</button>
<button @click="nextQuestion"></button>
</view>
</template>
<script setup lang="ts">
import {ref, computed, watch, watchEffect} from 'vue'
import {onLoad} from "@dcloudio/uni-app";
import UniBadge from "@/uni_modules/uni-badge/components/uni-badge/uni-badge.vue";
const zm=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
import moment from "moment/moment";
import path from "path";
import {info} from "sass";
//
const quizData = ref([])
const uid=ref("0")
//
const fujianuuid=ref("0")
const intervalId = ref<number | null>(null);
const activeIndex = ref(0)
const dati_able=ref(true)
//
const showResult = ref(false)
// /
const showAnswerCardFlag = ref(false)
const endtime=ref("")
//
const currentQuestion = computed(() => {
return quizData[activeIndex.value] || {}
})
// ID
const userAnswers = ref<Record<number, any>>({})
// true/false
const results = ref<boolean[]>([])
//
function handleRadioChange(id: number, value: string) {
userAnswers.value[id] = value
}
//
function prevQuestion() {
if (activeIndex.value > 0) activeIndex.value--
}
//
function nextQuestion() {
if (activeIndex.value < quizData.length - 1) activeIndex.value++
}
//
function goToQuestion(index: number) {
const id=quizData.value.findIndex(item=>item.id==index)
if(id!==-1) {
activeIndex.value = id
showAnswerCardFlag.value = false //
}
}
watch(quizData,function (newValue, oldValue) {
if(1===3) {
uni.request({
url: 'http://localhost:3400/api/updatezxdt_ex',
method: 'POST',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
alltm: newValue,
ksh: uid.value,
xuehao: "202413501062"
},
success: (res) => { /* ... */
console.log(res.data)
}
});
}
},{deep: true});
//
const totaltime=ref(5)
function getprint(question){
if(question.leixing==='填空题'){
let print =0;
JSON.parse(question.information).daan.map((daan, key) => {
print+=parseInt(daan.print);
})
return print;
}else if(question.leixing==='多选题'){
return parseInt(JSON.parse(question.information).df);
}else if(question.leixing==='单选题'){
return parseInt(JSON.parse(question.information).df);
}else if(question.leixing==='判断题'){
return parseInt(JSON.parse(question.information).df);
}
}
function submitAnswers() {
//
console.log(quizData.value[activeIndex.value])
if(quizData.value[activeIndex.value].leixing==="填空题"){
let print =0;
JSON.parse(quizData.value[activeIndex.value].information).daan.map((daan, key) => {
if(daan.daan===quizData.value[activeIndex.value].tk[key]){
print+=parseInt(daan.print);
}
})
console.log(print)
quizData.value[activeIndex.value].stuprint=print;
}
if(quizData.value[activeIndex.value].leixing==="多选题"){
let print =0;
let zq=true;
JSON.parse(quizData.value[activeIndex.value].information).daan.map((choose_one)=>{
if(!quizData.value[activeIndex.value].duoxuexiang.includes(choose_one)){
zq=false;
}
})
if(quizData.value[activeIndex.value].duoxuexiang.length!==JSON.parse(quizData.value[activeIndex.value].information).daan.length){
zq=false;
}
if(zq){
print=parseInt(JSON.parse(quizData.value[activeIndex.value].information).df)
}
console.log(print)
quizData.value[activeIndex.value].stuprint= print;
}else if(quizData.value[activeIndex.value].leixing==="单选题"){
if(quizData.value[activeIndex.value].xuexiang===JSON.parse(quizData.value[activeIndex.value].information).daan){
quizData.value[activeIndex.value].stuprint=parseInt(JSON.parse(quizData.value[activeIndex.value].information).df)
}else {
quizData.value[activeIndex.value].stuprint=0
}
}else if(quizData.value[activeIndex.value].leixing==="判断题"){
if(quizData.value[activeIndex.value].xuexiang===JSON.parse(quizData.value[activeIndex.value].information).daan){
quizData.value[activeIndex.value].stuprint=JSON.parse(quizData.value[activeIndex.value].information).df
}else {
quizData.value[activeIndex.value].stuprint=0
}
}
//////
if(1===3) {
results.value[activeIndex.value] = false
const currentQuestion = quizData[activeIndex.value]
const userAnswer = userAnswers.value[currentQuestion.id] ?? ''
const correctAnswer = String(currentQuestion.answer)
const normalizedUserAnswer = String(userAnswer).trim().toUpperCase()
const normalizedCorrectAnswer = correctAnswer.trim().toUpperCase()
const isCorrect = normalizedUserAnswer == normalizedCorrectAnswer
results.value[activeIndex.value] = isCorrect
showResult.value = true
}
}
//
const score = computed(() => {
const currentResult = results.value[activeIndex.value]
return currentResult ? currentQuestion.value.score : 0
})
//
const correctAnswer = computed(() => {
const currentQuestion = quizData[activeIndex.value]
if (!currentQuestion) return ''
const correctOption = currentQuestion.options.find(
option => option.value === currentQuestion.answer
)
return correctOption ? correctOption.text : ''
})
function pdt_one(xx:string,index:number){
quizData.value[index].daan = xx
}
const startTimer = () => {
intervalId.value = setInterval(() => {
if (totaltime.value>0) {
totaltime.value=moment(endtime.value).diff(moment(),'seconds')
} else {
stopTimer();
}
}, 1000);
};
//
const stopTimer = () => {
if (intervalId.value !== null) {
clearInterval(intervalId.value);
intervalId.value = null;
}
};
function danchoose(index: number,choose:string) {
console.log(choose);
console.log(index)
quizData.value[index].daan = choose
}
function duochoose(index: number,choose:string) {
console.log(choose);
console.log(index)
if(quizData.value[index].daan.includes(choose)) {
quizData.value[index].daan = quizData.value[index].daan.filter(item => item !== choose);
}else {
quizData.value[index].daan.push(choose);
}
}
//
function showAnswerCard() {
showAnswerCardFlag.value = true
}
function isNumber(value) {
return typeof value === 'number';
}
function updatwfujianuuid(){
uni.request({
url: 'http://localhost:3400/helpwx/uuid',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
}, success: (res) => { /* ... */
console.log(res.data)
fujianuuid.value=res.data
}
})
}
function tkt_one(xx:string,index:number){
quizData.value[index].sr=xx;
}
onLoad((options) => {
console.log(options)
uid.value=options.uuid;
updatwfujianuuid()
uni.request({
url: 'http://localhost:3400/kaoshi/select',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
ks:options.uuid,
xuehao:"202413501062"
},
success: (res) => { /* ... */
console.log(res.data)
let temp=[...res.data.infor]
res.data.infor.map((value,index)=>{
if(value.lx==="多选题"){
if (!temp[index].daan)
temp[index].daan=[]
}else if(value.lx==="简答题"){
if (!temp[index].filelist)
temp[index].filelist=[]
}
})
console.log(temp)
quizData.value=temp;
console.log(quizData)
if(res.data.end==="end"){
// setTishi("")
dati_able.value=false
//clearInterval(timer)
}else {
// const seconds=JSON.parse(res.data.information).seconds;
// let end=moment(res.data.stustart)
// end.add(seconds,'seconds');
// endtime.value=end.format('YYYY-MM-DD HH:mm:ss');
// console.log(end.format('YYYY-MM-DD HH:mm:ss'))
startTimer();
}
}
});
})
function returndati(){
showAnswerCardFlag.value = false
}
// function uppic(imageUrl,uuid) {
// return new Promise((resolve, reject) => {
// uni.chooseMedia({
// count: 1, //
// mediaType: ['image'], //
// sourceType: ['album', 'camera'], //
// sizeType: ['original', 'compressed'], //
// success(res) {
// const tempFilePath = res.tempFiles[0].tempFilePath
// uni.uploadFile({
// url: imageUrl, //
// filePath: tempFilePath, //
// name: 'file', //
// header: {
// accept: 'application/json',
// th: uid.value ,
// xx: uuid,
// lx: "kaoshi"
// },
// success: (uploadFileRes) => { //
// console.log(uploadFileRes);
// let src=uid.value + "/" + uuid + path.extname(tempFilePath)
//
// resolve({value1:uploadFileRes,src:src,lujing:res.tempFiles[0].tempFilePath})
// }
// });
// }
// })
// })
// }
function scpic(){
//console.log(uppic("http://localhost:3400/api/uploadtm",fujianuuid.value))
}
</script>
<style scoped>
.quiz-container {
padding: 5px;
position: relative;
}
/* 顶部栏样式 */
.top-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 3px;
border-bottom: 1px solid #e0e0e0;
background-color: #d97e7e;
}
.top-bar1 {
display: inline;
justify-content: space-between;
align-items: center;
padding: 3px;
border-bottom: 1px solid #e0e0e0;
background-color: #d97e7e;
width: 100vw;
}
.question-text1 {
font-size: 16px;
color: #333;
font-weight: bold;
margin-left: 10px;
}
.answer-card-btn1 {
background-color: #63a2cf;
border: 1px solid #a8d5ff;
color: #000;
padding: 3px 6px;
border-radius: 4px;
font-size: 12px;
margin-left: auto;
}
/* 新增左侧竖线装饰 */
.question-info1::before {
content: '';
display: inline-block;
width: 4px;
height: 20px;
background-color: #007bff;
margin-right: 3px;
}
.question-text {
font-size: 16px;
color: #333;
font-weight: bold;
margin-left: 10px;
}
.answer-card-btn {
background-color: #63a2cf;
border: 1px solid #a8d5ff;
color: #000;
padding: 3px 6px;
border-radius: 4px;
font-size: 12px;
background-color: #4490ca;
margin-left: auto;
}
/* 新增左侧竖线装饰 */
.question-info::before {
content: '';
display: inline-block;
width: 4px;
height: 20px;
background-color: #007bff;
margin-right: 3px;
}
.question {
margin-bottom: 20px;
display: flex;
flex-direction: column; /* 设置主轴为纵向 */
}
.question-text {
font-size: 16px;
margin-bottom: 10px;
}
.option {
display: flex;
align-items: center;
margin: 5px 0;
}
.option-image {
width: 50px;
height: 50px;
margin-left: 10px;
}
.result {
margin-top: 20px;
}
.answer-card {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 20px;
flex-direction: column;
}
.card-item-choose {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
width: 20px;
height: 20px;
background-color: #0ef306;
margin-left: 20px;
}
.card-item-nochoose {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
width: 20px;
height: 20px;
background-color: #fcffea;
margin-left: 20px;
}
.card-item-choose-noprint {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
width: 20px;
height: 20px;
background-color: #fbfbfb;
}
.score-and-answer {
margin-top: 20px;
background-color: #f0f0f0;
padding: 10px;
border-radius: 8px;
}
.bottom-controls {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 5px;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: 0 -2px 6px rgba(0, 0, 0, 0.1);
}
.bottom-controls button {
padding: 5px 10px;
font-size: 14px;
border-radius: 4px;
background-color: #f0f0f0;
color: #333;
border: none;
transition: all 0.3s ease;
}
.bottom-controls button:hover {
background-color: #e0e0e0;
}
.bottom-controls button[type="primary"] {
background-color: #2196f3;
color: #fff;
}
.question-image{
margin-left: 35px;
}
.bottom-controls button[type="primary"]:hover {
background-color: #1976d2;
}
.choose-button{
font-size: 18px;
position: absolute;
left: 3vw;
font-weight: bold;
height: 14vw;
width: 14vw;
border-radius: 50%;
}
.choose-wborpic{
position: relative;
left: 17vw;
top: 1vh;
}
.choose-image{
height: 12vh;
max-width: 70vw;
position: absolute;
left: 0;
top: -2vh;
}
.choose-xx{
margin-bottom: 40px;
}
.tkt-input {
border: 1rpx solid black;
border-radius: 8rpx;
padding: 10rpx 20rpx;
margin: 20rpx 0;
width: 60vw;
color: #00f0ff;
display: inline-block;
position: relative;
top: 24px;
left: 10px;
}
.xuhao{
width: 35px;
height: 35px;
border-radius: 50%;
background-color: #4a90e2;
display: inline-block;
}
.xuhao_wb{
display: inline-block;
font-size:25px;
margin-left: 6px;
}
.print-wb{
display: inline-block;
margin-left: 20px;
}
.grid-container-pd
{
display: flex;
flex-wrap: wrap;
margin-left: 60px;
}
.grid-container {
display: flex;
flex-wrap: wrap;
margin-left: 10vw;
}
.grid-item {
width: calc(25% - 10px); /* 每个项目宽度为25%,减去间距 */
margin: 5px; /* 项目之间的间距 */
box-sizing: border-box; /* 确保padding和border不会影响宽度 */
}
.choose-button1{
margin-top: 20px;
font-size: 18px;
margin-left: 10px;
font-weight: bold;
height: 18vw;
width: 18vw;
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
border-radius: 50%;
}
.container1 {
padding: 20rpx;
background-color: #f5f5f5;
min-height: 100vh;
}
.user-info {
display: flex;
align-items: center;
padding: 30rpx;
background-color: #fff;
border-radius: 16rpx;
margin-bottom: 30rpx;
}
.avatar {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin-right: 30rpx;
}
.user-text {
display: flex;
flex-direction: column;
}
.username {
font-size: 36rpx;
font-weight: bold;
margin-bottom: 10rpx;
}
.user-id {
font-size: 28rpx;
color: #999;
}
.score-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
}
.card-header {
margin-bottom: 30rpx;
}
.card-title {
font-size: 32rpx;
font-weight: bold;
display: block;
margin-bottom: 10rpx;
}
.card-subtitle {
font-size: 26rpx;
color: #999;
}
.score-content {
display: flex;
justify-content: space-between;
}
.score-item {
flex: 1;
text-align: center;
}
.score-label {
font-size: 28rpx;
color: #666;
display: block;
margin-bottom: 10rpx;
}
.score-value {
font-size: 40rpx;
font-weight: bold;
color: #07c160;
}
.dtk-left{
display:flex ;
flex-direction:column;
}
.dtk-right {
display:flex;
position: absolute; /* 绝对定位 */
right: 5px; /* 右对齐 */
align-items: end; /* 使子元素垂直对齐到容器的底部 */
}
.dtk-top {
display: flex;
width: 100vw;
}
.dtk-print-stu{
font-size: 25px;
}
.dtk-print-all{
font-size: 11px;
position: relative;
top: 12px;
}
.dtk-print-title{
font-size: 11px;
position: relative;
top: 12px;
margin-right: 20px;
}
.stuname{
}
.stuxuehao{
font-size: 10px;
}
.tm-list{
display: flex;
}
.return-button{
width: 80vw;
background-color: #ffffff;
color: green;
border: 3px solid green;
}
.duoxuanti_all{
}
.danxuanti_all{
display: flex;
flex-direction: row;
}
</style>

@ -0,0 +1,223 @@
<template>
<div class="question-list">
<ul>
<li
v-for="question in questions"
:key="question.uuid"
:class="['question-item', { selected: question.selected }]"
@click="handleSelect(question.uuid)"
>
<div class="info">
<span class="title">{{ JSON.parse(question.xx).name }}</span>
<div class="meta">
<span class="teacher">教师{{ question.teaname}}</span>
<span class="time">时间{{ moment(JSON.parse(question.xx).timestart).format('YYYY-MM-DD HH:mm:ss') }} - {{ moment(JSON.parse(question.xx).timeend).format('YYYY-MM-DD HH:mm:ss') }}</span>
</div>
</div>
<div class="stats">
<span class="num">{{question.paper? JSON.parse(question.paper).length:0 }}</span>
<span class="total">总分{{ allprint(question.std) }}</span>
</div>
</li>
</ul>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import {onLoad} from "@dcloudio/uni-app";
import moment from "moment";
//
interface Question {
uuid: string
title: string
course: string
teacher: string
startTime: string
endTime: string
num: number
total: number
selected: boolean
}
function allprint(std){
const stdlist=JSON.parse(std)
let print =0;
stdlist.map((stdone,index)=>{
if(stdone.print){
print+=stdone.print;
}
})
return print;
}
//
const questions = ref([
{
uuid: '1',
title: '题目1',
course: '课程1',
teacher: '教师1',
startTime: '2023-10-01T08:00:00',
endTime: '2023-10-01T09:00:00',
num: 1,
total: 100,
selected: false
},
{
uuid: '2',
title: '题目2',
course: '课程2',
teacher: '教师2',
startTime: '2023-10-02T08:00:00',
endTime: '2023-10-02T09:00:00',
num: 2,
total: 200,
selected: false
}
])
onLoad((options)=>{
uni.request({
url: 'http://localhost:3400/kaoshi/selectlist',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
xuehao:'202413501062'
},
success: (res) => { /* ... */
console.log(res.data)
questions.value=res.data
}
});
})
//
const handleSelect = (uuid) => {
console.log(uuid)
uni.navigateTo({
url: '/pages/pages_A/exam/exam?uuid='+uuid
});
}
//
const formatTime = (time: string): string => {
const date = new Date(time)
return `${date.getHours()}:${date.getMinutes().toString().padStart(2, '0')}`
}
</script>
<style scoped>
.question-list {
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
min-height: 100vh;
padding: 40px 20px;
color: #fff;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.question-list h2 {
text-align: center;
font-size: 2rem;
margin-bottom: 20px;
color: #00ffff;
text-shadow: 0 0 10px #00ffff;
}
ul {
list-style-type: none;
padding: 0;
max-width: 900px;
margin: 0 auto;
}
.question-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px;
margin-bottom: 16px;
border-radius: 12px;
background: rgba(255, 255, 255, 0.05);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
backdrop-filter: blur(10px);
cursor: pointer;
transition: all 0.3s ease-in-out;
border-left: 4px solid transparent;
}
.question-item:hover {
transform: scale(1.02);
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 24px rgba(0, 255, 255, 0.2);
}
.question-item.selected {
background-color: rgba(0, 255, 255, 0.2);
border-left-color: #00ffff;
box-shadow: 0 0 10px #00ffff;
}
.info {
display: flex;
flex-direction: column;
}
.title {
font-size: 18px;
font-weight: bold;
color: #ffffff;
margin-bottom: 6px;
}
.meta {
display: flex;
flex-wrap: wrap;
gap: 8px;
font-size: 12px;
color: #ccc;
}
.meta span {
padding: 4px 8px;
border-radius: 6px;
font-weight: 500;
}
.course {
background-color: rgba(0, 255, 255, 0.2);
color: #00ffff;
}
.teacher {
background-color: rgba(255, 255, 0, 0.2);
color: #fffa86;
}
.time {
background-color: rgba(255, 255, 255, 0.15);
color: #ddd;
width: 60vw;
}
.stats {
display: flex;
flex-direction: column;
align-items: flex-end;
}
.num {
font-size: 12px;
background-color: rgba(0, 255, 127, 0.2);
color: #00ff7f;
padding: 4px 8px;
border-radius: 6px;
}
.total {
font-size: 12px;
background-color: rgba(255, 105, 180, 0.2);
color: #ff69b4;
padding: 4px 8px;
border-radius: 6px;
margin-top: 6px;
}
</style>

@ -0,0 +1,35 @@
<template>
<!-- 基础分隔线 -->
<view class="divider-line"></view>
<!-- 带文本的分隔线 -->
</template>
<style scoped>
/* 基础分隔线 */
.divider-line {
height: 1rpx;
background-color: red;
margin: 20rpx 0;
}
/* 带文本的分隔线 */
.divider-with-text {
display: flex;
align-items: center;
margin: 30rpx 0;
}
.divider-with-text::before,
.divider-with-text::after {
content: '';
flex: 1;
height: 1rpx;
background: linear-gradient(90deg, transparent, #e4e7ed 50%, transparent);
}
.divider-text {
padding: 0 20rpx;
color: #909399;
font-size: 24rpx;
}
</style>

@ -0,0 +1,280 @@
<template>
<view class="container">
<!-- 页面内容 -->
<view class="page-content" v-if="kctivityAndPPT.length > 0">
<H4>课堂活动与 PPT 列表</H4>
<ul>
<li v-for="(item,index) in kctivityAndPPT" :key="item.id">
<view v-if="item.lx == 'ppt'">
<view class="content-ppt">
<view class="page-number">{{ currentPage }}</view>
<image :src="item.src" mode="aspectFit" />
</view>
<view class="footer">
<text class="time">{{ moment(item.time).fromNow() }}</text>
<text class="status">已完成</text>
</view>
</view>
<view v-else-if="item.lx == 'activity'">
<view class="activity-item" :style="{ backgroundColor:item.finish!=='finish' ? item.color : '#666' }" @click="()=>enterinclassactive(item)">
<view class="content" :style="{ backgroundColor: item.finish!=='finish' ? item.color : '#666' }">
<view class="title">{{ item.showname }}</view>
<view class="subtitle">{{ item.title }}</view>
</view>
<image class="icon" src="/static/student-course/course-active.png" mode="aspectFit"></image>
</view>
<view class="footer">
<text class="time">{{ moment(item.time).fromNow() }}</text>
<text class="status">{{ item.finish==="finish" ? item.finishwb : '' }}</text>
</view>
</view>
<view v-else-if="item.lx == 'notice'">
<view class="notice-view">
<text>{{ item.wb }}</text>
</view>
</view>
<view v-if="index!==kctivityAndPPT.length - 1">
<uv-divider
text="没有更多了"
:dashed="true"
:hairline="false"
text-position="center"
line-color="#e4e7ed"
text-color="#909399"
/>
</view>
</li>
</ul>
</view>
<view v-else>
<p>暂无课堂活动或 PPT 数据</p>
</view>
</view>
</template>
<script setup>
import { onMounted, onUnmounted } from 'vue'
import moment from "moment/min/moment-with-locales";
import io from '@hyoga/uni-socket.io'
import { ref } from 'vue'
import { onLoad } from "@dcloudio/uni-app";
import uvDivider from '@/pages_A/shangke/fgx.vue';
// and PPT
const kctivityAndPPT = ref([
{ id: 0, lx: 'notice', wb: '上课了' },
{ lx: 'ppt', id: 1, name: 'PPT展示1', src: '/static/course-image/ppt1.png', time: '2025-05-06T19:54:43+08:00' },
{ lx: 'activity', id: '181346ab-e4ca-4110-b92e-cca0fbea99c1', name: '课堂活动1', lxA: '讨论', color: 'green', showname: '课程讨论', title: '历史为什么选择了中国共产党', time: '2025-05-06T19:54:43+08:00' }
])
const finish = ref([1])
const currentPage = ref(1)
// socket
let socket
moment.locale('zh-cn');
//
const handleRefresh = (newId) => {
let index=kctivityAndPPT.value.findIndex(item => item.id === newId);
if(index!==-1){
kctivityAndPPT.value[index].finish="finish"
}
}
const handleRefresh_ex = (newId) => {
let index=kctivityAndPPT.value.findIndex(item => item.id === newId.uuid);
if(index!==-1){
kctivityAndPPT.value[index].finish="finish"
kctivityAndPPT.value[index].finishwb=newId.wb+"";
}
}
onLoad((options) => {
console.log('load', options)
console.log(kctivityAndPPT.value)
socket = null;
if (options) {
uni.request({
url: 'http://localhost:3400/apistu/getskxiang',
method: 'GET',
data: { // GET `data` `params` :ml-citation{ref="8" data="citationList"}
teacherid:'202413501',
kch:'n8z1A1',
xuehao:'202413501062'
},
success: (res) => { /* ... */
kctivityAndPPT.value = res.data
console.log(res.data)
}
});
socket = io('ws://localhost:3400', {
transports: ['websocket'],
auth: {
xuehao: "202413501062",
sf: "stu",
kch: options.kch,
kctime: options.kctime,
k_id: options.uuid,
}
})
} else {
socket = io('ws://localhost:3400', {
transports: ['websocket'],
auth: {
xuehao: "202413501062",
sf: "stu",
kch: "nokch",
kctime: "nokctime",
k_id: "nouuid",
}
})
}
//
uni.$on('finish_ex', handleRefresh_ex)
uni.$on('finish', handleRefresh)
socket.on('connect', () => console.log('Socket 已连接'))
socket.on('server', (data) => {
console.log(data)
})
socket.on('activeandppt', (data) => {
kctivityAndPPT.value.unshift(data)
})
socket.on('message', (data) => handleMessage(data))
})
//
onUnmounted(() => {
if (socket) {
socket.off('message')
socket.disconnect()
}
})
//
const handleMessage = (data) => {
//
console.log('处理消息:', data)
}
const enterinclassactive=(item)=>{
if(item.lxA == '讨论'){
uni.navigateTo({
url: '/pages/discussion/discussion?uuid=' +item.id
});
}else if(item.lxA=='签到'){
uni.navigateTo({
url: '/pages/checkin/checkin?uuid=' +item.id
});
}else if(item.lxA=='答题'){
uni.navigateTo({
url: '/pages/quiz/quiz?uuid=' +item.id
});
}else if(item.lxA=='抢答'){
uni.navigateTo({
url: '/pages/bussin/bussin?uuid=' +item.id
});
}
}
//
const sendData = () => {
if (socket) {
socket.emit('chat', { text: 'Hello' })
}
}
</script>
<style scoped>
.page-content{
/* margin-top: 20rpx; */
padding: auto;
background-color: #c0c0c0;
}
.container {
padding: 20rpx;
}
.classactivetitle {
font-size: 32rpx;
}
.content-ppt {
width: 100%;
height: auto;
border: 1px solid #4e01f5;
padding: 10rpx;
background-color: #ffffff;
box-sizing: border-box;
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
position: relative;
}
.activity-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6rpx;
border: 1px solid #000000;
background-color: #c5c3c3;
width: 93rvw;
}
.page-number {
position: absolute;
top: 10px;
right: 10px;
background-color: rgba(0, 0, 0, 0.5);
color: white;
padding: 5px 10px;
border-radius: 5px;
font-size: 14px;
z-index: 10;
}
.content {
flex: 1;
display: flex;
flex-direction: column;
background-color: #fff;
padding: 10px;
align-self: center;
}
.icon {
width: 50px;
height: 50px;
}
.title {
font-size: 18px;
color: white;
}
.subtitle {
font-size: 14px;
color: white;
}
.footer {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.time {
font-size: 12px;
color: #666;
}
.status {
font-size: 12px;
color: #666;
}
.notice-view {
display: flex;
justify-content: center; /* 水平居中 */
align-items: center; /* 垂直居中 */
background-color: #d3d3d3;
height: 23px; /* 可根据需求调整高度 */
font-size: 18px;
color: #333; /* 可根据需求调整字体大小 */
}
</style>

@ -0,0 +1,201 @@
<template>
<view class="container">
<!-- 页面内容 -->
<h3 >
上课
</h3>
<view v-if="kctivityAndPPT.length > 0">
<H4>课堂活动与 PPT 列表</H4>
<ul >
<li v-for="item in kctivityAndPPT" :key="item.id">
展示:课程活动
<view v-if="item.lx=='ppt'">
<view class="content" >
<image
:src="item.src"
mode="aspectFit"
/>
</view>
<view class="footer">
<text class="time">{{moment(item.time).fromNow()}}</text>
<text class="status">已完成</text>
</view>
</view>
<view v-else-if="item.lx=='activity'">
<view class="activity-item" :style="{backgroundColor: !finish.includes(item.id) ?item.color:'#666'}">
<view class="content" :style="{backgroundColor:!finish.includes(item.id) ?item.color:'#666'}">
<view class="title">{{ item.showname }}</view>
<view class="subtitle">{{ item.title }}</view>
</view>
<image class="icon" src="/src/static/student-course/course-active.png" mode="aspectFit"></image>
</view>
<view class="footer">
<text class="time">{{moment(item.time).fromNow()}}</text>
<text class="status">{{finish.includes(item.id)?'已完成':''}}</text>
</view>
<!-- <view :style="{backgroundColor:item.color,width:'90vw',height:'70px'}">
<view :style="{fontSize:'30px'}">
<text :style="{color:'white'}">{{item.name}}</text>
</view>
<view :style="{fontSize:'15px'}">
<text :style="{color:'white'}">{{item.title}}</text>
</view>
</view> -->
</view>
<view v-else-if="item.lx=='notice'">
<view class="notice-view">
<text >{{item.wb}}</text>
</view>
</view>
</li>
</ul>
</view>
<view v-else>
<p>暂无课堂活动或 PPT 数据</p>
</view>
</view>
</template>
<script setup>
import { onMounted, onUnmounted } from 'vue'
import moment from "moment/min/moment-with-locales";
import io from '@hyoga/uni-socket.io'
import { ref } from 'vue'
import {onLoad} from "@dcloudio/uni-app";
// 课堂活动and PPT
const kctivityAndPPT = ref([{id:0,lx:'notice',wb:'上课了'},
{lx:'ppt',id:1,name:'PPT展示1',src:'https://img11.360buyimg.com/n1/s720x720_jfs/t1/303510/33/1806/82651/6815a7a6F2e5d77b6/08d231a86927b0ce.jpg',time:'2025-05-06T19:54:43+08:00'},
{lx :'activity',id:2,name:'课堂活动1',lxA:'讨论',color:'green',showname:'课程讨论',title:'历史为什么选择了中国共产党',time:'2025-05-06T19:54:43+08:00'}
])
const finish=ref([1])
// 定义 socket 变量
let socket
moment.locale('zh-cn');
// 初始化连接
onLoad((options) => {
console.log('load',options)
console.log(kctivityAndPPT.value)
socket = null;
if(options) {
socket= io('ws://localhost:3400', {
transports: ['websocket'],
auth: {
xuehao: "202413501062",
sf: "stu",
kch: options.kch,
kctime: options.kctime,
k_id: options.uuid,
}
})
}else {
socket= io('ws://localhost:3400', {
transports: ['websocket'],
auth: {
xuehao: "202413501062",
sf: "stu",
kch: "nokch",
kctime: "nokctime",
k_id: "nouuid",
}
})
}
// 监听事件
socket.on('connect', () => console.log('Socket 已连接'))
socket.on('server', (data) => {
console.log(data)
})
socket.on('activeandppt', (data) => {
kctivityAndPPT.value.unshift(data)
})
socket.on('message', (data) => handleMessage(data))
})
// 断开连接并移除监听
onUnmounted(() => {
if (socket) {
socket.off('message')
socket.disconnect()
}
})
// 处理消息的方法
const handleMessage = (data) => {
// 处理消息逻辑
console.log('处理消息:', data)
}
// 发送数据的方法
const sendData = () => {
if (socket) {
socket.emit('chat', { text: 'Hello' })
}
}
</script>
<style scoped>
.container {
padding: 20rpx;
}
.classactivetitle {
font-size: 32rpx;
}
.activity-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px;
border: 1px solid #080808;
background-color: #c5c3c3;
width: 90vw;
}
.content{
flex: 1;
display: flex;
flex-direction: column;
background-color: #fff;
padding: 10px;
}
.icon {
width: 50px;
height: 50px;
}
.title {
font-size: 18px;
color: white;
}
.subtitle {
font-size: 14px;
color: white;
}
.footer {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.time {
font-size: 12px;
color: #666;
}
.status {
font-size: 12px;
color: #666;
}
.notice-view {
color:white;
display: flex;
background-color: #666;
align-items: center;
}
</style>

@ -0,0 +1,6 @@
export {}
declare module "vue" {
type Hooks = App.AppInstance & Page.PageInstance;
interface ComponentCustomOptions extends Hooks {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

@ -0,0 +1 @@
请准备一个二维码扫描图标PNG文件尺寸建议128x128像素蓝色科技风格与现有图标风格保持一致。

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@ -0,0 +1,76 @@
/**
* uni-app
*
* uni-app https://ext.dcloud.net.cn使
* 使scss使 import 便App
*
*/
/**
* App使
*
* 使scss scss 使 import
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color: #333; //
$uni-text-color-inverse: #fff; //
$uni-text-color-grey: #999; //
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #fff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; //
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); //
/* 边框颜色 */
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16;
/* 图片尺寸 */
$uni-img-size-sm: 20px;
$uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; //
/* 文章场景相关 */
$uni-color-title: #2c405a; //
$uni-font-size-title: 20px;
$uni-color-subtitle: #555; //
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; //
$uni-font-size-paragraph: 15px;

@ -0,0 +1,33 @@
## 1.2.22023-01-28
- 修复 运行/打包 控制台警告问题
## 1.2.12022-09-05
- 修复 当 text 超过 max-num 时badge 的宽度计算是根据 text 的长度计算,更改为 css 计算实际展示宽度,详见:[https://ask.dcloud.net.cn/question/150473](https://ask.dcloud.net.cn/question/150473)
## 1.2.02021-11-19
- 优化 组件UI并提供设计资源详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-badge](https://uniapp.dcloud.io/component/uniui/uni-badge)
## 1.1.72021-11-08
- 优化 升级ui
- 修改 size 属性默认值调整为 small
- 修改 type 属性,默认值调整为 errorinfo 替换 default
## 1.1.62021-09-22
- 修复 在字节小程序上样式不生效的 bug
## 1.1.52021-07-30
- 组件兼容 vue3如何创建vue3项目详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.1.42021-07-29
- 修复 去掉 nvue 不支持css 的 align-self 属性nvue 下不暂支持 absolute 属性
## 1.1.32021-06-24
- 优化 示例项目
## 1.1.12021-05-12
- 新增 组件示例地址
## 1.1.02021-05-12
- 新增 uni-badge 的 absolute 属性,支持定位
- 新增 uni-badge 的 offset 属性,支持定位偏移
- 新增 uni-badge 的 is-dot 属性,支持仅显示有一个小点
- 新增 uni-badge 的 max-num 属性,支持自定义封顶的数字值,超过 99 显示99+
- 优化 uni-badge 属性 custom-style 支持以对象形式自定义样式
## 1.0.72021-05-07
- 修复 uni-badge 在 App 端数字小于10时不是圆形的bug
- 修复 uni-badge 在父元素不是 flex 布局时宽度缩小的bug
- 新增 uni-badge 属性 custom-style 支持自定义样式
## 1.0.62021-02-04
- 调整为uni_modules目录规范

@ -0,0 +1,268 @@
<template>
<view class="uni-badge--x">
<slot />
<text v-if="text" :class="classNames" :style="[positionStyle, customStyle, dotStyle]"
class="uni-badge" @click="onClick()">{{displayValue}}</text>
</view>
</template>
<script>
/**
* Badge 数字角标
* @description 数字角标一般和其它控件列表9宫格等配合使用用于进行数量提示默认为实心灰色背景
* @tutorial https://ext.dcloud.net.cn/plugin?id=21
* @property {String} text 角标内容
* @property {String} size = [normal|small] 角标内容
* @property {String} type = [info|primary|success|warning|error] 颜色类型
* @value info 灰色
* @value primary 蓝色
* @value success 绿色
* @value warning 黄色
* @value error 红色
* @property {String} inverted = [true|false] 是否无需背景颜色
* @property {Number} maxNum 展示封顶的数字值超过 99 显示 99+
* @property {String} absolute = [rightTop|rightBottom|leftBottom|leftTop] 开启绝对定位, 角标将定位到其包裹的标签的四角上
* @value rightTop 右上
* @value rightBottom 右下
* @value leftTop 左上
* @value leftBottom 左下
* @property {Array[number]} offset 距定位角中心点的偏移量只有存在 absolute 属性时有效例如[-10, -10] 表示向外偏移 10px[10, 10] 表示向 absolute 指定的内偏移 10px
* @property {String} isDot = [true|false] 是否显示为一个小点
* @event {Function} click 点击 Badge 触发事件
* @example <uni-badge text="1"></uni-badge>
*/
export default {
name: 'UniBadge',
emits: ['click'],
props: {
type: {
type: String,
default: 'error'
},
inverted: {
type: Boolean,
default: false
},
isDot: {
type: Boolean,
default: false
},
maxNum: {
type: Number,
default: 99
},
absolute: {
type: String,
default: ''
},
offset: {
type: Array,
default () {
return [0, 0]
}
},
text: {
type: [String, Number],
default: ''
},
size: {
type: String,
default: 'small'
},
customStyle: {
type: Object,
default () {
return {}
}
}
},
data() {
return {};
},
computed: {
width() {
return String(this.text).length * 8 + 12
},
classNames() {
const {
inverted,
type,
size,
absolute
} = this
return [
inverted ? 'uni-badge--' + type + '-inverted' : '',
'uni-badge--' + type,
'uni-badge--' + size,
absolute ? 'uni-badge--absolute' : ''
].join(' ')
},
positionStyle() {
if (!this.absolute) return {}
let w = this.width / 2,
h = 10
if (this.isDot) {
w = 5
h = 5
}
const x = `${- w + this.offset[0]}px`
const y = `${- h + this.offset[1]}px`
const whiteList = {
rightTop: {
right: x,
top: y
},
rightBottom: {
right: x,
bottom: y
},
leftBottom: {
left: x,
bottom: y
},
leftTop: {
left: x,
top: y
}
}
const match = whiteList[this.absolute]
return match ? match : whiteList['rightTop']
},
dotStyle() {
if (!this.isDot) return {}
return {
width: '10px',
minWidth: '0',
height: '10px',
padding: '0',
borderRadius: '10px'
}
},
displayValue() {
const {
isDot,
text,
maxNum
} = this
return isDot ? '' : (Number(text) > maxNum ? `${maxNum}+` : text)
}
},
methods: {
onClick() {
this.$emit('click');
}
}
};
</script>
<style lang="scss" >
$uni-primary: #2979ff !default;
$uni-success: #4cd964 !default;
$uni-warning: #f0ad4e !default;
$uni-error: #dd524d !default;
$uni-info: #909399 !default;
$bage-size: 12px;
$bage-small: scale(0.8);
.uni-badge--x {
/* #ifdef APP-NVUE */
// align-self: flex-start;
/* #endif */
/* #ifndef APP-NVUE */
display: inline-block;
/* #endif */
position: relative;
}
.uni-badge--absolute {
position: absolute;
}
.uni-badge--small {
transform: $bage-small;
transform-origin: center center;
}
.uni-badge {
/* #ifndef APP-NVUE */
display: flex;
overflow: hidden;
box-sizing: border-box;
font-feature-settings: "tnum";
min-width: 20px;
/* #endif */
justify-content: center;
flex-direction: row;
height: 20px;
padding: 0 4px;
line-height: 18px;
color: #fff;
border-radius: 100px;
background-color: $uni-info;
background-color: transparent;
border: 1px solid #fff;
text-align: center;
font-family: 'Helvetica Neue', Helvetica, sans-serif;
font-size: $bage-size;
/* #ifdef H5 */
z-index: 999;
cursor: pointer;
/* #endif */
&--info {
color: #fff;
background-color: $uni-info;
}
&--primary {
background-color: $uni-primary;
}
&--success {
background-color: $uni-success;
}
&--warning {
background-color: $uni-warning;
}
&--error {
background-color: $uni-error;
}
&--inverted {
padding: 0 5px 0 0;
color: $uni-info;
}
&--info-inverted {
color: $uni-info;
background-color: transparent;
}
&--primary-inverted {
color: $uni-primary;
background-color: transparent;
}
&--success-inverted {
color: $uni-success;
background-color: transparent;
}
&--warning-inverted {
color: $uni-warning;
background-color: transparent;
}
&--error-inverted {
color: $uni-error;
background-color: transparent;
}
}
</style>

@ -0,0 +1,85 @@
{
"id": "uni-badge",
"displayName": "uni-badge 数字角标",
"version": "1.2.2",
"description": "数字角标(徽章)组件,在元素周围展示消息提醒,一般用于列表、九宫格、按钮等地方。",
"keywords": [
"",
"badge",
"uni-ui",
"uniui",
"数字角标",
"徽章"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
"type": "component-vue"
},
"uni_modules": {
"dependencies": ["uni-scss"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

@ -0,0 +1,10 @@
## Badge 数字角标
> **组件名uni-badge**
> 代码块: `uBadge`
数字角标一般和其它控件列表、9宫格等配合使用用于进行数量提示默认为实心灰色背景
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-badge)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

@ -0,0 +1,8 @@
## 1.0.32022-01-21
- 优化 组件示例
## 1.0.22021-11-22
- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题
## 1.0.12021-11-22
- 修复 vue3中scss语法兼容问题
## 1.0.02021-11-18
- init

@ -0,0 +1 @@
@import './styles/index.scss';

@ -0,0 +1,82 @@
{
"id": "uni-scss",
"displayName": "uni-scss 辅助样式",
"version": "1.0.3",
"description": "uni-sass是uni-ui提供的一套全局样式 通过一些简单的类名和sass变量实现简单的页面布局操作比如颜色、边距、圆角等。",
"keywords": [
"uni-scss",
"uni-ui",
"辅助样式"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": "^3.1.0"
},
"dcloudext": {
"category": [
"JS SDK",
"通用 SDK"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "u"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "n",
"联盟": "n"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

@ -0,0 +1,4 @@
`uni-sass``uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass)
#### 如使用过程中有任何问题或者您对uni-ui有一些好的建议欢迎加入 uni-ui 交流群871950839

@ -0,0 +1,7 @@
@import './setting/_variables.scss';
@import './setting/_border.scss';
@import './setting/_color.scss';
@import './setting/_space.scss';
@import './setting/_radius.scss';
@import './setting/_text.scss';
@import './setting/_styles.scss';

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save