finish 123
This commit is contained in:
parent
2bcc22bfe1
commit
0ea11b60fc
@ -1,7 +1,13 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:model_of_the_times/entities/ViewInformation.dart';
|
||||
import 'package:model_of_the_times/icons/material_design_icons.dart';
|
||||
import 'package:model_of_the_times/requester/requester.dart';
|
||||
import 'package:model_of_the_times/views/activity.dart';
|
||||
import 'package:model_of_the_times/views/common/data_required.dart';
|
||||
import 'package:model_of_the_times/views/home.dart';
|
||||
import 'package:model_of_the_times/views/learned.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
@ -16,10 +22,19 @@ class MyApp extends StatelessWidget {
|
||||
return MaterialApp(
|
||||
title: '时代楷模',
|
||||
theme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.white),
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.red, surface: Colors.white),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const MainPage(),
|
||||
home: DataRequired(
|
||||
fetchData: (global) async {
|
||||
var data = await global.requester.post(resolve("/app/login"), body: jsonEncode({'username': 'WUvFG3gY','password': 'ZogPgBF6'}));
|
||||
return jsonFromResponse(data);
|
||||
},
|
||||
afterLoading: (data){
|
||||
GlobalInformation.getInstance().token = data['token'];
|
||||
return const MainPage();
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -34,7 +49,9 @@ class MainPage extends StatefulWidget {
|
||||
class _MainPageState extends State<MainPage> {
|
||||
int _nowPageIndex = 0;
|
||||
final List<ViewInformation> _pages = [
|
||||
ViewInformation(const HomeView(), "时代楷模")
|
||||
ViewInformation(const HomeView(), "时代楷模"),
|
||||
ViewInformation(const Activity(), "公益活动"),
|
||||
ViewInformation(const Learned(), "学习心得")
|
||||
];
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -52,10 +69,14 @@ class _MainPageState extends State<MainPage> {
|
||||
body: page.view,
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
onTap: (newIndex){
|
||||
if (newIndex >= _pages.length) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_nowPageIndex = newIndex;
|
||||
});
|
||||
},
|
||||
currentIndex: _nowPageIndex,
|
||||
type: BottomNavigationBarType.fixed,
|
||||
items: const [
|
||||
BottomNavigationBarItem(
|
||||
@ -64,7 +85,7 @@ class _MainPageState extends State<MainPage> {
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(MaterialDesign.feedback),
|
||||
label: "公告"
|
||||
label: "公益"
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
icon: Icon(MaterialDesign.favorite),
|
||||
|
@ -10,7 +10,7 @@ class CustomRequesterClient extends BaseClient {
|
||||
if (information.token.isNotEmpty){
|
||||
request.headers.addAll({"Authorization": information.token});
|
||||
}
|
||||
if (request.headers.containsKey("Content-Type")){
|
||||
if (request.headers['Content-Type'] == 'text/plain; charset=utf-8'){
|
||||
request.headers['Content-Type'] = "application/json";
|
||||
}
|
||||
return client.send(request);
|
||||
@ -36,4 +36,8 @@ Uri resolve(String url){
|
||||
dynamic jsonFromResponse(Response response) {
|
||||
var str = utf8.decode(response.bodyBytes);
|
||||
return jsonDecode(str);
|
||||
}
|
||||
dynamic jsonFromStreamResponse(StreamedResponse response) async {
|
||||
var str = utf8.decode(await response.stream.toBytes());
|
||||
return jsonDecode(str);
|
||||
}
|
5
lib/utils/toast.dart
Normal file
5
lib/utils/toast.dart
Normal file
@ -0,0 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
void toast(String message, BuildContext context){
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message)));
|
||||
}
|
196
lib/views/activity.dart
Normal file
196
lib/views/activity.dart
Normal file
@ -0,0 +1,196 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:model_of_the_times/requester/requester.dart';
|
||||
import 'package:model_of_the_times/utils/toast.dart';
|
||||
import 'package:model_of_the_times/views/common/data_required.dart';
|
||||
import 'package:model_of_the_times/views/common/full_paged_struct.dart';
|
||||
|
||||
class ActivityCard extends StatefulWidget {
|
||||
final String picPath;
|
||||
final int id;
|
||||
final String startDate;
|
||||
final String endDate;
|
||||
final int status;
|
||||
final String title;
|
||||
final String content;
|
||||
final String sponsor;
|
||||
final int signUpNum;
|
||||
const ActivityCard({super.key, required this.picPath, required this.id, required this.startDate, required this.endDate, required this.status, required this.title, required this.content, required this.sponsor, required this.signUpNum});
|
||||
|
||||
@override
|
||||
State<ActivityCard> createState() => _ActivityCardState();
|
||||
}
|
||||
|
||||
class _ActivityCardState extends State<ActivityCard> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(padding: const EdgeInsets.all(10), child: Column(
|
||||
children: [
|
||||
Image.network(resolve(widget.picPath).toString(), height: 100, fit: BoxFit.fitHeight),
|
||||
ListTile(
|
||||
title: Text(widget.title),
|
||||
subtitle: Column(
|
||||
children: [
|
||||
Text("活动时间:${widget.startDate} - ${widget.endDate}"),
|
||||
Text("发起方:${widget.sponsor}"),
|
||||
Text("简介:${widget.content}", maxLines: 1, overflow: TextOverflow.ellipsis)
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Flex(
|
||||
direction: Axis.horizontal,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: [
|
||||
const Icon(Icons.add),
|
||||
Text("已报名${widget.signUpNum}人")
|
||||
],
|
||||
),
|
||||
OutlinedButton(onPressed: (){
|
||||
if (widget.status == 1) {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) => ActivityDetails(id: widget.id)));
|
||||
}
|
||||
}, child: Text(widget.status == 1 ? "去报名" : widget.status == 2 ? "报名截止" : "已报名"))
|
||||
],
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Activity extends StatefulWidget {
|
||||
const Activity({super.key});
|
||||
|
||||
@override
|
||||
State<Activity> createState() => _ActivityState();
|
||||
}
|
||||
|
||||
class _ActivityState extends State<Activity> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DataRequired(
|
||||
fetchData: (global) async {
|
||||
var req = global.requester;
|
||||
var response = await req.get(resolve("/activity/app-o/list"));
|
||||
return jsonFromResponse(response);
|
||||
},
|
||||
afterLoading: (data) {
|
||||
if (kDebugMode) {
|
||||
print(data);
|
||||
}
|
||||
data['rows'].sort((a,b){return (a['status'] - b['status'] )as int;});
|
||||
return ListView(
|
||||
children: data['rows'].map<Widget>((e) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 10),
|
||||
child: ActivityCard(picPath: e['picPath'], id: e['id'], startDate: e['startDate'], endDate: e['endDate'], status: e['status'], title: e['title'], content: e['content'], sponsor: e['sponsor'], signUpNum: e['signUpNum']),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ActivityDetails extends StatefulWidget {
|
||||
final int id;
|
||||
const ActivityDetails({super.key, required this.id});
|
||||
|
||||
@override
|
||||
State<ActivityDetails> createState() => _ActivityDetailsState();
|
||||
}
|
||||
|
||||
class _ActivityDetailsState extends State<ActivityDetails> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FullPagedStruct(
|
||||
title: "活动详情",
|
||||
child: DataRequired(
|
||||
fetchData: (global) async {
|
||||
return jsonFromResponse(await global.requester.get(resolve("/activity/app-o/detail?id=${widget.id}")));
|
||||
},
|
||||
afterLoading: (data) {
|
||||
data = data['data'];
|
||||
return Stack(
|
||||
children: [
|
||||
ListView(
|
||||
children: [
|
||||
Image.network(resolve(data['picPath']).toString()),
|
||||
ListTile(
|
||||
title: Text(data['title']),
|
||||
subtitle: Column(
|
||||
children: [
|
||||
Text("活动时间:${data['startDate']} - ${data['endDate']}"),
|
||||
Text("报名截止时间:${data['signUpEndDate']}"),
|
||||
Text("发起方:${data['sponsor']}"),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Flex(
|
||||
direction: Axis.vertical,
|
||||
children: [
|
||||
const Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
child: VerticalDivider(thickness: 8),
|
||||
),
|
||||
Text("活动详情"),
|
||||
],
|
||||
),
|
||||
Text(data['content'])
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
Positioned.fill(
|
||||
bottom: 0,
|
||||
child: Flex(
|
||||
direction: Axis.horizontal,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: [
|
||||
Text(data['signUpNum'].toString()),
|
||||
const Text("人已报名")
|
||||
],
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
var data = jsonFromResponse(await GlobalInformation.getInstance().requester.get(resolve("/activity/app/signUp?id=${widget.id}")));
|
||||
if (kDebugMode) {
|
||||
print(data);
|
||||
}
|
||||
if (data['code'] == 200){
|
||||
if (context.mounted){
|
||||
Navigator.pop(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (context.mounted){
|
||||
toast("报名失败,请重试!", context);
|
||||
}
|
||||
},
|
||||
child: const Text("报名")
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -11,10 +11,10 @@ class DataRequired extends StatefulWidget {
|
||||
const DataRequired({super.key, required this.fetchData, required this.afterLoading, this.height = -1, this.width = -1});
|
||||
|
||||
@override
|
||||
State<DataRequired> createState() => _DataRequiredState();
|
||||
State<DataRequired> createState() => DataRequiredState();
|
||||
}
|
||||
|
||||
class _DataRequiredState extends State<DataRequired> {
|
||||
class DataRequiredState extends State<DataRequired> {
|
||||
bool isLoading = true;
|
||||
dynamic data = {};
|
||||
void _fetch() async {
|
||||
@ -70,4 +70,8 @@ class _DataRequiredState extends State<DataRequired> {
|
||||
}
|
||||
return const Center(child: Text("加载失败,网络错误!"));
|
||||
}
|
||||
|
||||
void update() {
|
||||
_fetch();
|
||||
}
|
||||
}
|
||||
|
20
lib/views/common/full_paged_struct.dart
Normal file
20
lib/views/common/full_paged_struct.dart
Normal file
@ -0,0 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:model_of_the_times/icons/material_design_icons.dart';
|
||||
|
||||
class FullPagedStruct extends StatelessWidget {
|
||||
final Widget child;
|
||||
final String title;
|
||||
const FullPagedStruct({super.key, required this.child, required this.title});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: IconButton(onPressed: (){Navigator.pop(context);}, icon: const Icon(MaterialDesign.keyboard_arrow_left)),
|
||||
title: Text(title),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Padding(padding: const EdgeInsets.all(10), child: child),
|
||||
);
|
||||
}
|
||||
}
|
@ -53,8 +53,6 @@ class _HomeViewState extends State<HomeView> {
|
||||
bool onChange(index) {
|
||||
var func = functions[index];
|
||||
var ret = func(context);
|
||||
print(func);
|
||||
print(ret);
|
||||
if (ret != null){
|
||||
setState(() {
|
||||
lastWidget = ret;
|
||||
|
263
lib/views/learned.dart
Normal file
263
lib/views/learned.dart
Normal file
@ -0,0 +1,263 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:model_of_the_times/requester/requester.dart';
|
||||
import 'package:model_of_the_times/utils/toast.dart';
|
||||
import 'package:model_of_the_times/views/common/data_required.dart';
|
||||
import 'package:model_of_the_times/views/common/full_paged_struct.dart';
|
||||
|
||||
class Learned extends StatefulWidget {
|
||||
const Learned({super.key});
|
||||
|
||||
@override
|
||||
State<Learned> createState() => _LearnedState();
|
||||
}
|
||||
|
||||
class _LearnedState extends State<Learned> with SingleTickerProviderStateMixin {
|
||||
late final TabController _controller;
|
||||
int _index = 0;
|
||||
List<Widget> items = [
|
||||
const Testimonials(),
|
||||
const LearnHistory()
|
||||
];
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TabController(length: 2, vsync: this);
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: TabBar(
|
||||
controller: _controller,
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
labelPadding: const EdgeInsetsDirectional.all(10),
|
||||
tabs: const [
|
||||
Text("学习感言"),
|
||||
Text("学习历史"),
|
||||
],
|
||||
onTap: (e){
|
||||
setState(() {
|
||||
_index = e;
|
||||
});
|
||||
},
|
||||
),
|
||||
body: items[_index],
|
||||
floatingActionButton: _index == 0 ? FloatingActionButton.extended(onPressed: (){Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { return const AddTestimonials(); }));}, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), label: const Text("新建感言")) : null,
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Testimonial extends StatefulWidget {
|
||||
final int id;
|
||||
final String title;
|
||||
final String content;
|
||||
final GlobalKey<DataRequiredState> parentKey;
|
||||
const Testimonial({super.key, required this.id, required this.title, required this.content, required this.parentKey});
|
||||
|
||||
@override
|
||||
State<Testimonial> createState() => _TestimonialState();
|
||||
}
|
||||
|
||||
class _TestimonialState extends State<Testimonial> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Slidable(
|
||||
endActionPane: ActionPane(
|
||||
motion: const ScrollMotion(),
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: (e){
|
||||
var req = GlobalInformation.getInstance().requester;
|
||||
req.get(resolve("/appStudy/app/deleteStatement?id=${widget.id}")).then((value) {
|
||||
var data = jsonFromResponse(value);
|
||||
if (data['code'] == 200){
|
||||
widget.parentKey.currentState?.update();
|
||||
return;
|
||||
}
|
||||
if (context.mounted){
|
||||
toast("message", context);
|
||||
}
|
||||
});
|
||||
},
|
||||
backgroundColor: Colors.red,
|
||||
label: "删除",
|
||||
spacing: 1,
|
||||
)
|
||||
]
|
||||
),
|
||||
child: Card(
|
||||
child: Padding(padding: const EdgeInsets.all(10), child: ListTile(
|
||||
title: Text(
|
||||
widget.title
|
||||
),
|
||||
subtitle: Text(
|
||||
widget.content,
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
)),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Testimonials extends StatefulWidget {
|
||||
const Testimonials({super.key});
|
||||
|
||||
@override
|
||||
State<Testimonials> createState() => _TestimonialsState();
|
||||
}
|
||||
|
||||
class _TestimonialsState extends State<Testimonials> {
|
||||
final GlobalKey<DataRequiredState> _key = GlobalKey();
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DataRequired(
|
||||
key: _key,
|
||||
fetchData: (global) async {
|
||||
var response = await global.requester.get(resolve("/appStudy/app/statementList"));
|
||||
return jsonFromResponse(response);
|
||||
},
|
||||
afterLoading: (data) {
|
||||
return ListView(
|
||||
children: data['rows'].map<Widget>((e){
|
||||
var content = e['content'];
|
||||
var id = e['id'];
|
||||
var title = e['title'];
|
||||
return Testimonial(id: id, title: title, content: content, parentKey: _key);
|
||||
}).toList()
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TestimonialsEditor extends StatefulWidget {
|
||||
final String defaultTitle;
|
||||
final String defaultContent;
|
||||
const TestimonialsEditor({super.key, this.defaultContent = "", this.defaultTitle = ""});
|
||||
|
||||
@override
|
||||
State<TestimonialsEditor> createState() => _TestimonialsEditorState();
|
||||
}
|
||||
|
||||
class _TestimonialsEditorState extends State<TestimonialsEditor> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late String _title;
|
||||
late String _content;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_title = widget.defaultTitle;
|
||||
_content = widget.defaultContent;
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
TextFormField(
|
||||
initialValue: _title,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "标题"
|
||||
),
|
||||
validator: (value){
|
||||
if (value == null || value.isEmpty){
|
||||
return "请输入标题";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {if (value != null){_title = value;}},
|
||||
),
|
||||
TextFormField(
|
||||
initialValue: _content,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "内容"
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty){
|
||||
return "请输入内容";
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onSaved: (value) {if (value != null){_content = value;}},
|
||||
),
|
||||
SizedBox.fromSize(size: const Size.fromHeight(20),),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (_formKey.currentState == null){
|
||||
return;
|
||||
}
|
||||
if (!_formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
_formKey.currentState!.save();
|
||||
var global = GlobalInformation.getInstance();
|
||||
var createUri = resolve("appStudy/app/createStatement");
|
||||
var req = MultipartRequest("POST", createUri);
|
||||
req.fields.addAll({"title": _title, "content": _content, "picPath": ""});
|
||||
var result = await global.requester.send(req);
|
||||
var data = await jsonFromStreamResponse(result);
|
||||
if (context.mounted){
|
||||
if (data['code'] != 200){
|
||||
toast("添加失败,请重试!", context);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
child: const Text("提交")
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class AddTestimonials extends StatefulWidget {
|
||||
const AddTestimonials({super.key});
|
||||
|
||||
@override
|
||||
State<AddTestimonials> createState() => _AddTestimonialsState();
|
||||
}
|
||||
|
||||
class _AddTestimonialsState extends State<AddTestimonials> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const FullPagedStruct(title: "新建感言", child: TestimonialsEditor());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class LearnHistory extends StatefulWidget {
|
||||
const LearnHistory({super.key});
|
||||
|
||||
@override
|
||||
State<LearnHistory> createState() => _LearnHistoryState();
|
||||
}
|
||||
|
||||
class _LearnHistoryState extends State<LearnHistory> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DataRequired(
|
||||
fetchData: (global) async {
|
||||
var req = global.requester;
|
||||
var historyResponse = await req.get(resolve("/appStudy/app/historyList"));
|
||||
return jsonFromResponse(historyResponse);
|
||||
},
|
||||
afterLoading: (data){
|
||||
if (kDebugMode) {
|
||||
print(data);
|
||||
}
|
||||
return ListView();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -70,6 +70,14 @@ packages:
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
flutter_slidable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_slidable
|
||||
sha256: "2c5611c0b44e20d180e4342318e1bbc28b0a44ad2c442f5df16962606fd3e8e3"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
|
@ -36,6 +36,7 @@ dependencies:
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.6
|
||||
http: ^1.2.2
|
||||
flutter_slidable: ^3.1.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
x
Reference in New Issue
Block a user