feat: add view_assets page — browse media by group with bricks widgets
- init.py: add rl_list_assets_client() — validates org ownership, syncs from vendor, returns asset list with status/url/type - view_assets.ui: dropdown to select vendor_group_id, submit triggers asset list display - submit_list_assets.dspy: returns responsive card grid with Image/ VideoPlayer/AudioPlayer widgets, per-asset refresh status + download - index.ui: add '查看素材' card for customers - load_path.py: register view_assets.ui under logined RBAC
This commit is contained in:
parent
daf82107fb
commit
6491182249
@ -584,6 +584,52 @@ async def rl_sync_asset_status_user(org_id, asset_id, user_id):
|
||||
return {"success": True, "status": status, "url": url}
|
||||
|
||||
|
||||
async def rl_list_assets_client(org_id, vendor_group_id):
|
||||
"""Client API: List assets for a vendor_group_id, sync from vendor first."""
|
||||
dbname = _get_dbname()
|
||||
db = DBPools()
|
||||
|
||||
# Validate org owns this group, get vendor + local_group_id
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
grp_recs = await sor.R("rl_org_group", {
|
||||
"org_id": org_id,
|
||||
"vendor_group_id": vendor_group_id,
|
||||
})
|
||||
if not grp_recs:
|
||||
return {"success": False, "message": "无效的认证组合或无权访问"}
|
||||
vendor = grp_recs[0].vendor
|
||||
local_group_id = grp_recs[0].local_group_id
|
||||
|
||||
# Sync from vendor to get fresh status
|
||||
try:
|
||||
await rl_sync_assets_from_vendor(org_id, local_group_id)
|
||||
except Exception as e:
|
||||
debug(f"rl_list_assets_client: vendor sync failed: {e}")
|
||||
|
||||
# Query local assets
|
||||
async with db.sqlorContext(dbname) as sor:
|
||||
recs = await sor.R("rl_asset", {
|
||||
"org_id": org_id,
|
||||
"group_id": local_group_id,
|
||||
})
|
||||
if not recs:
|
||||
return {"success": True, "assets": []}
|
||||
|
||||
assets = []
|
||||
for r in recs:
|
||||
assets.append({
|
||||
"id": r.id,
|
||||
"name": getattr(r, "name", ""),
|
||||
"status": getattr(r, "status", ""),
|
||||
"url": getattr(r, "url", ""),
|
||||
"asset_type": getattr(r, "asset_type", ""),
|
||||
"vendor_asset_id": getattr(r, "vendor_asset_id", ""),
|
||||
"create_time": getattr(r, "create_time", ""),
|
||||
})
|
||||
|
||||
return {"success": True, "assets": assets}
|
||||
|
||||
|
||||
async def rl_handle_callback(byted_token, project_name="default"):
|
||||
"""
|
||||
Callback handler: vendor POSTs here after H5 auth completes.
|
||||
@ -719,6 +765,7 @@ def load_reallife_asset():
|
||||
g.rl_verify_user = rl_verify_user
|
||||
g.rl_upload_user = rl_upload_user
|
||||
g.rl_sync_asset_status_user = rl_sync_asset_status_user
|
||||
g.rl_list_assets_client = rl_list_assets_client
|
||||
g.rl_handle_callback = rl_handle_callback
|
||||
g.rl_query_groups = rl_query_groups
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ PATHS_LOGINED = [
|
||||
# 客户可用页面
|
||||
f"/{MOD}/create_validate.ui",
|
||||
f"/{MOD}/upload_asset.ui",
|
||||
f"/{MOD}/view_assets.ui",
|
||||
|
||||
# API — 所有 api/ 下的 .dspy(脚本内部通过 get_user() 做权限校验)
|
||||
f"/{MOD}/api/%",
|
||||
|
||||
146
wwwroot/api/submit_list_assets.dspy
Normal file
146
wwwroot/api/submit_list_assets.dspy
Normal file
@ -0,0 +1,146 @@
|
||||
|
||||
vendor_group_id = params_kw.get('vendor_group_id', '')
|
||||
|
||||
if not vendor_group_id:
|
||||
return json.dumps({
|
||||
"widgettype": "Error",
|
||||
"options": {"title": "错误", "message": "请选择认证组合"}
|
||||
})
|
||||
|
||||
org_id = (await get_userorgid()) or '0'
|
||||
|
||||
result = await rl_list_assets_client(org_id, vendor_group_id)
|
||||
|
||||
if not result.get('success'):
|
||||
return json.dumps({
|
||||
"widgettype": "Error",
|
||||
"options": {"title": "查询失败", "message": result.get('message', '未知错误')}
|
||||
})
|
||||
|
||||
assets = result.get('assets', [])
|
||||
|
||||
if not assets:
|
||||
return json.dumps({
|
||||
"widgettype": "VBox",
|
||||
"id": "rl_asset_results",
|
||||
"options": {"padding": "16px", "gap": "12px"},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {"text": "该认证组合下暂无素材,请先上传素材。"}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
# Build asset cards
|
||||
cards = []
|
||||
for a in assets:
|
||||
status = a.get('status', '')
|
||||
name = a.get('name', '')
|
||||
url = a.get('url', '')
|
||||
atype = a.get('asset_type', '')
|
||||
create_time = str(a.get('create_time', ''))[:16]
|
||||
asset_id = a.get('id', '')
|
||||
vendor_asset_id = a.get('vendor_asset_id', '')
|
||||
|
||||
# Status icon
|
||||
s_lower = status.lower() if status else ''
|
||||
if s_lower in ('active', 'available', 'ready'):
|
||||
status_icon = "✅"
|
||||
elif s_lower in ('processing', 'pending', 'submitted'):
|
||||
status_icon = "⏳"
|
||||
elif s_lower in ('failed', 'error'):
|
||||
status_icon = "❌"
|
||||
else:
|
||||
status_icon = "📋"
|
||||
|
||||
# Build card subwidgets
|
||||
card_subs = [
|
||||
{
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "8px", "alignItems": "center", "marginBottom": "4px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": name or "未命名", "fontWeight": "bold"}},
|
||||
{"widgettype": "Text", "options": {"text": f"{status_icon} {status}", "cfontsize": 0.9}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {"text": f"类型: {atype} 创建: {create_time}", "cfontsize": 0.9}
|
||||
}
|
||||
]
|
||||
|
||||
# Media preview
|
||||
if url:
|
||||
if atype == 'Image':
|
||||
card_subs.append({
|
||||
"widgettype": "Image",
|
||||
"options": {"url": url, "cwidth": 15, "cheight": 10, "objectFit": "cover", "borderRadius": "4px", "marginTop": "8px"}
|
||||
})
|
||||
elif atype == 'Video':
|
||||
card_subs.append({
|
||||
"widgettype": "VideoPlayer",
|
||||
"options": {"url": url, "cwidth": 20, "cheight": 12, "marginTop": "8px"}
|
||||
})
|
||||
elif atype == 'Audio':
|
||||
card_subs.append({
|
||||
"widgettype": "AudioPlayer",
|
||||
"options": {"url": url, "marginTop": "8px"}
|
||||
})
|
||||
|
||||
# Refresh status button (always show for processing assets, optional for others)
|
||||
check_url = request.path.rsplit('/', 1)[0] + '/submit_query_status.dspy'
|
||||
btn_subs = [
|
||||
{
|
||||
"widgettype": "Button",
|
||||
"options": {"label": "刷新状态"},
|
||||
"binds": [{
|
||||
"wid": "self", "event": "click",
|
||||
"actiontype": "script", "target": "self",
|
||||
"script": "(async function(){" \
|
||||
"var url='" + check_url + "?_webbricks_=1&asset_id=" + asset_id + "';" \
|
||||
"var r=await fetch(url);" \
|
||||
"var j=await r.json();" \
|
||||
"await bricks.show_resp_message_or_error({json:async function(){return j}});" \
|
||||
"})()"
|
||||
}]
|
||||
}
|
||||
]
|
||||
if url:
|
||||
btn_subs.append({
|
||||
"widgettype": "Button",
|
||||
"options": {"label": "下载"},
|
||||
"binds": [{
|
||||
"wid": "self", "event": "click",
|
||||
"actiontype": "script", "target": "self",
|
||||
"script": "window.open('" + url + "', '_blank')"
|
||||
}]
|
||||
})
|
||||
card_subs.append({
|
||||
"widgettype": "HBox",
|
||||
"options": {"gap": "8px", "marginTop": "8px"},
|
||||
"subwidgets": btn_subs
|
||||
})
|
||||
|
||||
cards.append({
|
||||
"widgettype": "VBox",
|
||||
"options": {"css": "card", "padding": "12px", "borderRadius": "8px"},
|
||||
"subwidgets": card_subs
|
||||
})
|
||||
|
||||
# Wrap cards in ResponsableBox for responsive grid
|
||||
result_widget = {
|
||||
"widgettype": "VBox",
|
||||
"id": "rl_asset_results",
|
||||
"options": {"padding": "16px", "gap": "12px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"text": f"共 {len(assets)} 个素材", "fontWeight": "bold"}},
|
||||
{
|
||||
"widgettype": "ResponsableBox",
|
||||
"options": {"gap": "12px", "minWidth": "300px"},
|
||||
"subwidgets": cards
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
return json.dumps(result_widget, ensure_ascii=False)
|
||||
@ -121,6 +121,44 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"css": "card",
|
||||
"cheight": 12,
|
||||
"cwidth": 23,
|
||||
"padding": "20px",
|
||||
"cursor": "pointer",
|
||||
"borderRadius": "8px"
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "click",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.rl_content",
|
||||
"options": {
|
||||
"url": "{{entire_url('view_assets.ui')}}"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
],
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Title5",
|
||||
"options": {
|
||||
"text": "🖼️ 查看素材"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Text",
|
||||
"options": {
|
||||
"text": "按认证组合查看已上传的素材及状态",
|
||||
"cfontsize": 1.2
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
{% endif %}
|
||||
{% if is_admin %}
|
||||
|
||||
54
wwwroot/view_assets.ui
Normal file
54
wwwroot/view_assets.ui
Normal file
@ -0,0 +1,54 @@
|
||||
{% set current_group = params_kw.get('vendor_group_id', '') %}
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {
|
||||
"width": "100%",
|
||||
"padding": "16px"
|
||||
},
|
||||
"subwidgets": [
|
||||
{
|
||||
"widgettype": "Title4",
|
||||
"options": {
|
||||
"text": "查看素材",
|
||||
"fontWeight": "600",
|
||||
"marginBottom": "16px"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widgettype": "Form",
|
||||
"id": "view_assets_form",
|
||||
"options": {
|
||||
"submit_url": "{{entire_url('api/submit_list_assets.dspy')}}",
|
||||
"fields": [
|
||||
{
|
||||
"uitype": "code",
|
||||
"name": "vendor_group_id",
|
||||
"label": "认证组合",
|
||||
"dataurl": "{{entire_url('api/get_org_groups.dspy')}}",
|
||||
"data_field": "value",
|
||||
"text_field": "text",
|
||||
"required": true,
|
||||
"value": "{{ current_group }}"
|
||||
}
|
||||
]
|
||||
},
|
||||
"binds": [
|
||||
{
|
||||
"wid": "self",
|
||||
"event": "submited",
|
||||
"actiontype": "urlwidget",
|
||||
"target": "app.rl_asset_results",
|
||||
"options": {
|
||||
"method": "POST"
|
||||
},
|
||||
"mode": "replace"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"id": "rl_asset_results",
|
||||
"options": {"padding": "16px"}
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user