opt portal

This commit is contained in:
jingganjiaoyu 2024-07-28 23:20:53 +08:00
parent 5c435b39ab
commit 48bedf9b2c
18 changed files with 111 additions and 209 deletions

View File

@ -1,12 +1,13 @@
binarys = bin/ice.bin
version = src/version.go
binpack = src/binpack.go
option = src/option.go
flags = -ldflags "-w -s" -v
all: def
@date +"%Y-%m-%d %H:%M:%S"
go build ${flags} -o ${binarys} src/main.go ${version} ${binpack} && ./${binarys} forever restart &>/dev/null
go build ${flags} -o ${binarys} src/main.go ${option} ${version} ${binpack} && ./${binarys} forever restart &>/dev/null
def:
@[ -f ${version} ] || echo "package main">${version}
@[ -f ${binpack} ] || echo "package main">${binpack}
@[ -f ${binpack} ] || echo "package main">${binpack}

View File

@ -1,11 +1,11 @@
# Contexts
Contexts 通过模块化、集群化、自动化的方式,只用一个 20M 大小的程序文件,就可以在各种设备上,一键启动完整的云计算服务与云研发环境。
# Education
Education 通过模块化、集群化、自动化的方式,只用一个 20M 大小的程序文件,就可以在各种设备上,一键启动完整的云计算服务与云研发环境。
## 源码安装
### 克隆编译
```sh
git clone https://shylinux.com/x/contexts
cd contexts; source etc/miss.sh
git clone https://shylinux.com/x/education
cd education; source etc/miss.sh
```
### 启动服务
@ -16,4 +16,4 @@ ish_miss_serve
### 访问网页
```sh
open http://localhost:9020
```
```

View File

@ -1,4 +1,2 @@
~web.code.db
config database driver mysql
~ssh
source local.shy

View File

@ -12,4 +12,6 @@ ish_miss_prepare_project
ish_miss_prepare_contexts
ish_miss_prepare_resource
ish_miss_make; [ -z "$*" ] || ish_miss_serve "$@"
ish_miss_prepare community
ish_miss_make; [ -z "$*" ] || ish_miss_serve "$@"

2
go.mod
View File

@ -1,6 +1,6 @@
module shylinux.com/x/education
go 1.13
go 1.20
require (
shylinux.com/x/golang-story v0.0.23

View File

@ -2,17 +2,15 @@ package jiaowuxitong
import (
"shylinux.com/x/ice"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/education/src/jiaowuxitong/model"
)
type Class struct {
Table
create string `name:"create school_uid* grade* name*"`
}
type Class struct{ Table }
func (s Class) Inputs(m *ice.Message, arg ...string) {
switch arg[0] {
case mdb.NAME:
case model.NAME:
m.Push(arg[0], "高一二班")
m.Push(arg[0], "高二三班")
m.Push(arg[0], "初一一班")

View File

@ -1,10 +1,7 @@
package jiaowuxitong
import (
"strings"
"shylinux.com/x/ice"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/icebergs/base/web"
kit "shylinux.com/x/toolkits"
@ -14,7 +11,6 @@ import (
type Table struct {
db.Table
create string `name:"create name*"`
}
func (s Table) Init(m *ice.Message, arg ...string) {
@ -22,16 +18,14 @@ func (s Table) Init(m *ice.Message, arg ...string) {
}
func (s Table) Inputs(m *ice.Message, arg ...string) {
switch arg[0] {
case model.USER_UID, model.CLASS_UID, model.SCHOOL_UID:
m.Optionv(mdb.SELECT, model.UID, model.NAME)
m.Cmdy(m.Prefix(strings.TrimSuffix(arg[0], "_uid"))).RenameAppend(model.UID, arg[0])
m.DisplayInputKeyNameIconTitle()
case GRADE:
case model.GRADE:
y := kit.Int(kit.Split(m.Time(), "-")[0])
for i := 0; i < 10; i++ {
m.Push(arg[0], kit.Format("%d级", y))
y--
}
default:
s.Table.Inputs(m, arg...)
}
}

View File

@ -0,0 +1,31 @@
package jiaowuxitong
import (
"shylinux.com/x/ice"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/community/src/gonganxitong"
"shylinux.com/x/education/src/jiaowuxitong/model"
)
const (
EVENT_CLASS_CREATE = "web.team.education.class.create"
EVENT_CLASS_REMOVE = "web.team.education.class.remove"
)
type events struct {
user gonganxitong.User
portal portal
classCreate string `name:"classCreate" event:"web.team.education.class.create"`
classRemove string `name:"classRemove" event:"web.team.education.class.remove"`
}
func (s events) ClassCreate(m *ice.Message, arg ...string) { s.classEvent(m, "班级创建成功") }
func (s events) ClassRemove(m *ice.Message, arg ...string) { s.classEvent(m, "班级删除成功") }
func init() { ice.Cmd(prefixKey(), events{}) }
func (s events) classEvent(m *ice.Message, arg ...string) {
m.Cmd(s.user, s.user.SendTemplate, m.MergePodCmd("", ice.GetTypeKey(s.portal))+"#"+m.Option(model.CLASS_UID),
m.Trans(m.ActionKey(), arg[0]), m.Option(model.CLASS_NAME), kit.Cut(m.Option(model.CLASS_UID), 8))
}

View File

@ -10,25 +10,12 @@ const (
NAME = "name"
GRADE = "grade"
USER_UID = "user_uid"
USER_NAME = "user_name"
CLASS_UID = "class_uid"
CLASS_NAME = "class_name"
SCHOOL_UID = "school_uid"
SCHOOL_NAME = "school_name"
)
type Sess struct {
db.ModelWithUID
UserUID string `gorm:"type:char(32)"`
IP string `gorm:"type:char(16)"`
Agent string `gorm:"size:char(256)"`
}
type User struct {
db.ModelWithUID
OpenID string `gorm:"type:char(32)"`
Avatar string `gorm:"size:char(256)"`
Name string `gorm:"type:char(32)"`
}
type UserClass struct {
db.Model
UserUID string `gorm:"type:char(32);index"`
@ -55,7 +42,7 @@ type Homework struct {
type models struct{ db.Models }
func (s models) Init(m *ice.Message, arg ...string) {
s.Models.Register(m, &Sess{}, &User{}, &UserClass{}, &Class{}, &School{}, &Homework{})
s.Models.Register(m, "jiaowuxitong", &UserClass{}, &Class{}, &School{}, &Homework{})
}
func init() { ice.Cmd("web.team.education.jiaowuxitong.models", models{}) }

View File

@ -1,15 +1 @@
$output { background-color:var(--plugin-bg-color); }
$output>div.list { border-radius:10px; background-color:var(--output-bg-color); padding:10px; margin:10px; }
$output>div.list>div.title { font-weight:bold; display:flex; align-items:center; }
$output>div.list>div.title span:first-child { flex-grow:1; }
$output>div.list>div.item.card { padding:10px 0; display:flex; }
$output>div.list>div.item.card>img { height:36px; }
$output>div.list>div.item.card div.title { font-size:16px; }
$output>div.list>div.item.card div.title span { margin-right:5px; }
$output>div.list>div.item.card div.status { color:var(--disable-fg-color); font-size:12px; }
$output>div.list>div.item.card div.status span { margin-right:5px; }
$output>div.list>div.item.index { padding:10px; display:flex; flex-direction:column; align-items:center; float:left; }
$output>div.list>div.item.index img { width:100%; }
$output>div.action div.item.button { margin-right:5px; }
$output>div.action div.item.button input { border:none; color:var(--notice-bg-color); min-width:80px; float:left; }
$output>div.action div.item.button span { display:none; }
$output { background-color:var(--plugin-bg-color); }

View File

@ -6,16 +6,16 @@ import (
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/toolkits"
"shylinux.com/x/community/src/gonganxitong"
"shylinux.com/x/education/src/jiaowuxitong/model"
)
type portal struct {
ice.Hash
Class Class
gonganxitong.Portal
user gonganxitong.User
userClass userClass
export string `data:"true"`
short string `data:"index"`
field string `data:"time,name,icons,index,order,enable"`
Class Class
school school
list string `name:"list class_uid index uid auto" role:"void"`
classCreate string `name:"classCreate school_uid* grade* name*" role:"void"`
classRemove string `name:"classRemove class_uid*" role:"void"`
@ -26,32 +26,40 @@ func (s portal) Inputs(m *ice.Message, arg ...string) {
}
func (s portal) List(m *ice.Message, arg ...string) {
if len(arg) == 0 {
m.Cmdy(s.userClass, s.userClass.MyClass, s.UserUID(m)).Action(s.ClassCreate).PushAction(s.ClassRemove)
m.Cmdy(s.userClass, s.userClass.MyClass, s.user.UserUID(m)).Action(s.ClassCreate).PushAction(s.ClassRemove)
kit.If(m.Length() == 0, func() { m.EchoInfoButton(m.Trans("Please Create Your Class", "请创建班级"), s.ClassCreate) })
} else if len(arg) == 1 {
s.Hash.List(m, arg[1:]...).SortInt(mdb.ORDER)
} else if len(arg) == 2 {
m.Cmdy(ctx.COMMAND, arg[1]).Push(ctx.ARGS, arg[0])
m.Display("").DisplayCSS("")
} else {
m.FieldsSetDetail().Cmdy(arg[1], mdb.SELECT, model.UID, arg[2]).PushAction().Action()
s.Portal.List(m, arg...)
}
m.Display("").DisplayCSS("")
}
func (s portal) ClassCreate(m *ice.Message, arg ...string) {
if !m.Cmdy(s.Class, s.Class.Create, arg).IsErr() {
m.Cmdy(s.userClass, s.userClass.Create, model.USER_UID, s.UserUID(m), model.CLASS_UID, m.Result())
m.ToastProcess()
msg := m.Cmd(s.school, s.school.Select, model.UID, m.Option(model.SCHOOL_UID))
if !m.WarnNotFound(msg.Length() == 0, "school") {
if !m.Cmdy(s.Class, s.Class.Create, arg).IsErr() {
args := kit.Simple(model.USER_UID, s.user.UserUID(m), model.CLASS_UID, m.Result())
m.Cmd(s.userClass, s.userClass.Create, args)
m.Event(EVENT_CLASS_CREATE, args, model.CLASS_NAME, m.Option(mdb.NAME))
m.ProcessRefresh().ToastSuccess()
}
}
}
func (s portal) ClassRemove(m *ice.Message, arg ...string) {
m.Cmdy(s.Class, s.Class.Delete, model.UID, m.Option(model.CLASS_UID))
m.Cmdy(s.userClass, s.userClass.Delete, model.USER_UID, s.UserUID(m), model.CLASS_UID, m.Option(model.CLASS_UID))
m.ToastProcess()
args := kit.Simple(model.USER_UID, s.user.UserUID(m), m.OptionSimple(model.CLASS_UID))
msg := m.Cmd(s.userClass, s.userClass.Select, args)
if !m.WarnNotFound(msg.Length() == 0, "class") {
m.Cmdy(s.userClass, s.userClass.Delete, args)
m.Cmdy(s.Class, s.Class.Delete, model.UID, m.Option(model.CLASS_UID))
m.Event(EVENT_CLASS_REMOVE, args)
m.ProcessRefresh().ToastSuccess()
}
}
func init() { ice.Cmd(prefixKey(), portal{}) }
func (s portal) UserUID(m *ice.Message) string {
return m.Option(ice.MSG_USERNAME)
}
func (s portal) Show(m *ice.Message, arg ...string) {
cmd := m.GetCommand()
m.Cmd(s, s.Create, mdb.NAME, cmd.Help, mdb.ICONS, cmd.Icon, ctx.INDEX, m.PrefixKey())

View File

@ -1,72 +1,17 @@
var UID = "uid", CLASS_UID = "class_uid"
var UID = "uid", CLASS_UID = "class_uid", CLASS_NAME = "class_name"
Volcanos(chat.ONIMPORT, {
_init: function(can, msg) { can.user.isMobile && can.isCmdMode() && can.onappend.style(can, html.OUTPUT)
if (can.Option(CLASS_UID) == "") {
if (can.db.hash.length > 1 && can.db.hash[0]) { return can.Option(CLASS_UID, can.db.hash[0]), can.Option(ctx.INDEX, can.db.hash[1]), can.Update() }
can.ui = can.page.Append(can, can._output, ["myclass.list", "myindex.list"])
can.page.Append(can, can.ui.myclass, [{view: html.TITLE, list: [{text: can.user.trans(can, "My Class", "我的班级")}, can.page.button(can, "classCreate")]}])
can.run({}, [can.onimport.myClass(can, msg, can.ui.myclass)], function(msg) {
can.page.Append(can, can.ui.myindex, [{view: html.TITLE, list: [{text: can.user.trans(can, "My Index", "我的课程")}]}])
can.onimport.myIndex(can, msg, can.ui.myindex)
can.onimport.myIndex(can, msg, can.ui.myindex)
can.onimport.myIndex(can, msg, can.ui.myindex)
can.onimport.myIndex(can, msg, can.ui.myindex)
can.onimport.myIndex(can, msg, can.ui.myindex)
can.onimport.myIndex(can, msg, can.ui.myindex)
can.onimport.myIndex(can, msg, can.ui.myindex)
})
} else {
can.onimport.myData(can, msg, can._output)
}
},
myClass: function(can, msg, target) { var class_uid
can.page.Append(can, target||can._output, msg.Table(function(value) {
class_uid = class_uid||value.class_uid, value.class_uid == can.onexport.session(can, CLASS_UID) && (class_uid = value.class_uid)
class_uid == value.class_uid && can.onexport.title(can, value.class_name)
return {view: [[html.ITEM_CARD]], list: [
{img: can.misc.ResourceIcons(can, value.icon)},
{view: ice.INFO, list: [
{view: html.TITLE, list: [{text: value.class_name}]},
{view: html.STATUS, list: [{text: value.grade}, {text: value.school_name}]},
]},
], onclick: function(event) {
can.onexport.hash(can, value.class_uid), can.onexport.session(can, CLASS_UID, value.class_uid)
can.onexport.title(can, value.class_name), can.user.toastSuccess(can, "switch")
can.user.agent.init(can, can.user.info.titles)
}}
})), can.onexport.hash(can, class_uid), can.onexport.session(can, CLASS_UID, class_uid); return class_uid
},
myIndex: function(can, msg, target) {
can.page.Append(can, target||can._output, msg.Table(function(value) {
var width; can.user.isMobile && !can.user.isLandscape() && (width = (can.ConfWidth()-40)/4)
return {view: [[html.ITEM, ctx.INDEX]], style: {width: width}, list: [
{img: can.misc.ResourceIcons(can, value.icon)}, {text: can.user.trans(can, value.index.split(".").pop(), value.name)},
], onclick: function(event) {
can.Option(CLASS_UID, can.onexport.session(can, CLASS_UID)), can.Option(ctx.INDEX, value.index), can.Update()
}}
}))
},
myData: function(can, msg, target) {
msg.Table(function(value) { function back() {} function refresh() {}
var list = [can.page.button(can, can.user.trans(can, "goback", "返回"), function(event) { back() }), can.page.button(can, can.user.trans(can, "reload", "刷新"), function(event) { refresh() })]
var ui = can.page.Append(can, can._output, [{view: html.ACTION}])
if (can.db.hash.length > 2 && can.db.hash[0] && can.db.hash[2]) { value.args = [can.db.hash[0], can.db.hash[2]] }
value.style = html.OUTPUT, value.height = can.ConfHeight()-html.ACTION_HEIGHT
can.onappend.plugin(can, value, function(sub) { refresh = function() { sub.Update() }
sub.onexport.output = function(_sub, msg) { can.user.toastSuccess(can, "load", "", 1000)
can.page.Appends(can, ui.action, list), msg.Option(ice.MSG_ACTION) && can.onappend._action(sub, msg.Option(ice.MSG_ACTION), ui.action, null, true)
can.onexport.hash(can, can.Option(CLASS_UID), value.index, sub.Option(UID))
back = function() {
if (sub.Option(UID)) {
can.onexport.hash(can, can.Option(CLASS_UID), can.Option(ctx.INDEX))
sub.Option(UID, ""), sub.Update()
} else {
can.onexport.hash(can, "")
can.Option(CLASS_UID, ""), can.Option(ctx.INDEX, ""), can.Update()
}
}
}
})
_init: function(can, msg) {
can.require([
"usr/community/src/gonganxitong/portal.js",
"usr/community/src/gonganxitong/portal.css?render=replace&index="+can.ConfIndex(),
], function() {
can.onimport.myPortal(can, msg, CLASS_UID, CLASS_NAME, "我的班级")
})
},
})
myValue: function(can, value) {
return [
{view: html.TITLE, list: [{text: value.class_name}]},
{view: html.STATUS, list: [{text: value.class_uid.slice(0, 8)}, {text: value.school_name}, {text: value.grade}]},
]
},
})

View File

@ -9,10 +9,10 @@
"icons": {
"classCreate": "bi bi-plus-square-dotted"
},
"style": {
"classRemove": "danger"
},
"input": {
"migrate": "迁移",
"uid": "主键",
"info": "信息",
"address": "地址",
"grade": "入学年份",
"open_id": "外键",

View File

@ -2,13 +2,9 @@ package jiaowuxitong
import "shylinux.com/x/ice"
const (
GRADE = "grade"
)
type school struct {
Table
create string `name:"create name* info address"`
create string `name:"create name* address info"`
}
func init() { ice.Cmd(prefixKey(), school{}) }

View File

@ -1,27 +0,0 @@
package jiaowuxitong
import (
"shylinux.com/x/ice"
"shylinux.com/x/icebergs/base/aaa"
"shylinux.com/x/icebergs/base/web"
kit "shylinux.com/x/toolkits"
"shylinux.com/x/education/src/jiaowuxitong/model"
)
type sess struct {
Table
create string `name:"create user_uid*"`
check string `name:"check uid*"`
}
func (s sess) Create(m *ice.Message, arg ...string) {
s.Table.Create(m, model.USER_UID, m.Option(model.USER_UID), aaa.IP, m.Option(ice.MSG_USERIP), web.AGENT, m.Option(ice.MSG_USERUA))
m.ProcessCookie(ice.MSG_SESSID, kit.JoinWord(web.SPACE, m.Option(ice.MSG_USERPOD), m.PrefixKey(), aaa.CHECK, m.Result()), "-2")
}
func (s sess) Check(m *ice.Message, arg ...string) {
msg := s.Table.Select(m.Spawn(), model.UID, arg[0])
m.Option(ice.MSG_USERNAME, msg.Append(model.USER_UID))
}
func init() { ice.Cmd(prefixKey(), sess{}) }

View File

@ -1,30 +0,0 @@
package jiaowuxitong
import (
"shylinux.com/x/ice"
"shylinux.com/x/icebergs/base/mdb"
"shylinux.com/x/education/src/jiaowuxitong/model"
)
const (
OPEN_ID = "open_id"
OPENID = "openid"
AVATAR = "avatar"
)
type user struct {
Table
create string `name:"create openid*"`
}
func (s user) Create(m *ice.Message, arg ...string) {
if s.Table.Select(m, OPEN_ID, m.Option(OPENID)).Length() == 0 {
s.Table.Create(m, OPEN_ID, m.Option(OPENID), AVATAR, m.Option("headimgurl"), mdb.NAME, m.Option("nickname"))
m.Option(model.USER_UID, m.Result())
} else {
m.Option(model.USER_UID, m.Append(model.UID))
}
}
func init() { ice.Cmd(prefixKey(), user{}) }

View File

@ -9,4 +9,4 @@ import (
func main() { print(ice.Run()) }
func init() { ice.Info.Titles = "云教育" }
func init() { ice.Info.Titles = "云教育" }

View File

@ -0,0 +1,13 @@
package {{.Option "zone"}}
import "shylinux.com/x/ice"
type {{.Option "name"}} struct {
Table
}
func (s {{.Option "name"}}) List(m *ice.Message, arg ...string) {
s.Table.List(m, arg...)
}
func init() { ice.Cmd(prefixKey(), {{.Option "name"}}{}) }