您现在的位置是:首页 > cms教程 > Discuz教程Discuz教程
Discuz!NT在线用户功能简介
尤政航2025-07-17Discuz教程已有人查阅
导读在上文(Discuz!NT URL地址重写) 中, 聊到了“在线用户”功能,因为当时介绍的重点不是“在线”那一块,所以没做深入介绍。这就为今天这篇文章埋下了“伏笔”。
在上文(Discuz!NT URL地址重写) 中, 聊到了“在线用户”功能,因为当时介绍的重点不是“在线”那一块,所以没做深入介绍。这就为今天这篇文章埋下了“伏笔”。因为在线这个功能太重要了,大家不妨用VS打开我们产品的较新源码,然后搜索一下“OnlineUsers.”这个内容就会看到它在产品中被使用的“频率”。
好了,言归正传,下面就开始接着上一篇文章中所说的“OnlineUsers.ResetOnlineList();” 方法介绍一下用户在线功能。
首先请大家打开Discuz.Forum这个项目,从中找到"OnlineUsers.cs"这个文件,打开它找到"ResetOnlineList()"这个函数,它的代码如下:
好了,即然清楚了这个函数的作用,不妨再了解一下代码中InitOnlineList()方法,它的作用就是运行下面这个SQL语句:
上文介绍的仅仅是系统“初始化”时所要做的“活”。而更重要的内容是用户从一访问论坛并执行一系列操作(如登陆,发贴,浏览版块等)时,“在线用户”机制在里面所启的重要作用。这里先以登陆(系统)这一操作发生时,程序所做出的响应为例,看一下在线这块是如何进行绑定的,请看如下代码(位于discuz.web项目的“aspx/1/”文件夹下的login.aspx.cs文件):
OnlineUsers.UpdateAction(olid, UserAction.Login.ActionID, 0, config.Onlinetimeout);
它的作用就是将已成功登陆系统的用户所执行的当前动作及相关信息更新到"dnt_onlines" 表中。而这个函数本身所用到的四个参数要重点介绍一下:
olid: 在线列表id,对应"dnt_onlines"表中的olid字段
action:动作(结构类型,详情参见discuz.forum项目下的forumutils.cs文件)用于传递动作的相关信息如:ActionName动作名称和ActionDescription动作描述等)
inid: 所在位置代码,即当前用户所访问的主题(TopicId)。因为登陆不牵扯主题操作,所以上面的值为0
timeout: 无动作离线时间(config.Onlinetimeout),这个数据是在后台进行设置的,
timeout的作用就是当有别人访问论坛时,在更新自身在线状态信息同时,用这个设置数据(整型)与在线表(dnt_onlines)中的"lastupdatetime"字段(之后活动时间)进行比较,找出超过
timeout规定的时间的用户,将其在线状态(onlinestate)设置为0(即为离线)。而做这件事的函数就是在项目“discuz.data.sqlserver”下的“UserManage.cs”中的“AddOnlineUser”函数,这里将它的代码贴出来,详情见注释:
(因为在线表经常有数据添加进来)。所以在上面加入了"olid > 2147483000"的逻辑判断,来预防这个问题的出现。
说了一大堆的关于timeout参数的问题,而另一个重要的参数olid还没作详细介绍呢:)上面所述的登陆页面(login.aspx.cs)中,olid是从basepage类中获得的,而basepage就是前台主要页面的“基类”,它里面的构造函数部分封装了页面中大部分公共变量的初始化操作。所以要了解olid,还要从basepage.cs中分析一把。请看下面的代码段(摘自discuz.web.ui项目下的basepage.cs文件):
来进行实始化绑定的,所以我们还需要再看一下这个函数,相关代码段如下:
不存在(首次访问)则初始化相关(游客)用户信息。下面再介绍一下action这个参数,它是结构(struct)类型,里面的ActionName动作名称和ActionD-escription动作描述(目前未用上)是用于显示当前动作信息的。如果大家觉得没问题的话,下面再介绍一下注销(退出)操作,因为有登陆就会有退出。而注销这块的操作也很简单,请看如下代码段(位于aspx/1/logout.aspx.cs文件):
而“OnlineUsers.DeleteRows(olid);”这行代码其实就是删除在线表中指定olid的用户信息了。因
为代码过于简单,只是一条SQL语句而已,所以就不在这里多费笔默了。
其实关于在线表有关的操作还有很多,大家不妨挖一下onlines.cs这个文件,从中会有更多的收获。
之后再介绍一下有关“较大在线人数”这个问题,其实这个功能主要是为了中小站长(服务器资源相对紧张)提供的一项设置,旨在当用户在线人数达到一定数量时,拒绝其它的访问。相应的代码判断逻辑如下(摘自basepage.cs文件):
好了,言归正传,下面就开始接着上一篇文章中所说的“OnlineUsers.ResetOnlineList();” 方法介绍一下用户在线功能。
首先请大家打开Discuz.Forum这个项目,从中找到"OnlineUsers.cs"这个文件,打开它找到"ResetOnlineList()"这个函数,它的代码如下:
1/// <summary>
2/// 复位在线表, 如果系统未重启, 仅是应用程序重新启动, 则不会重新创建
3/// </summary>
4/// <returns></returns>
5public static int ResetOnlineList()
6{
7 try
8 {
9// 取得在线表之后一条记录的tickcount字段 (因为本功能不要求特别精确)
10//int tickcount = DatabaseProvider.GetInstance().GetLastTickCount();
11// 如果距离现在系统运行时间小于10分钟
12if (System.Environment.TickCount < 600000)
13{
14 return InitOnlineList();
15}
16return -1;
17 }
18 catch
19 {
20try
21{
22 return InitOnlineList();
23}
24catch
25{
26 return -1;
27}
28 }
29
30}
这个函数本身就是在系统启动之后的10分钟内运行InitOnlineList函数,而为什么是10分钟(不是别的时间段呢?),主要是因为这个值是个“估计值”,因为在线功能是系统的核心功能之一,换句话说,系统在启动10分钟内,只要是用户在前台进行操作,绝对会用到这个核心功能,而这个功能本身是依赖于数据库中的“dnt_onlines"表的(如果大家想了解这个表的结构,可以下载我们产品的“数据字典”,里面有这方面内容的介绍)。所以创建(复位)和初始化这个数据表的责任就交给了ResetOnlineList()这个函数(因为它是在HttpModule.cs中被绑定的,可以看作是系统运行的起点,详情见上文)。好了,即然清楚了这个函数的作用,不妨再了解一下代码中InitOnlineList()方法,它的作用就是运行下面这个SQL语句:
1//该函数位于Discuz.Data.SqlServer项目中的UserManage.cs文件中
2 public int CreateOnlineTable()
3{
4try
5{
6StringBuilder sb = new StringBuilder();
7sb.Append("IF EXISTS (SELECT * FROM SYSOBJECTS WHERE id = object_id(N'[dnt_online]') AND OBJECTPROPERTY(id, N'IsUserTable') = 1) DROP TABLE [dnt_online];");
8sb.Append("CREATE TABLE [dnt_online] ([olid] [int] IDENTITY (1, 1) NOT NULL,[userid] [int] NOT NULL,[ip] [varchar] (15) NOT NULL,[username] [nvarchar] (20) NOT NULL,[nickname] [nvarchar] (20) NOT NULL,[password] [char] (32) NOT NULL,[groupid] [smallint] NOT NULL,[olimg] [varchar] (80) NOT NULL,[adminid] [smallint] NOT NULL,[invisible] [smallint] NOT NULL,[action] [smallint] NOT NULL,[lastactivity] [smallint] NOT NULL,[lastposttime] [datetime] NOT NULL,[lastpostpmtime] [datetime] NOT NULL,[lastsearchtime] [datetime] NOT NULL,[lastupdatetime] [datetime] NOT NULL,[forumid] [int] NOT NULL,[forumname] [nvarchar] (50) NOT NULL,[titleid] [int] NOT NULL,[title] [nvarchar] (80) NOT NULL,[verifycode] [varchar] (10) NOT NULL ) ON [PRIMARY];");
9sb.Append("ALTER TABLE [dnt_online] WITH NOCHECK ADD CONSTRAINT [PK_dnt_online] PRIMARY KEY CLUSTERED ([olid]) ON [PRIMARY]; ");
10sb.Append("ALTER TABLE [dnt_online] ADD CONSTRAINT [DF_dnt_online_userid] DEFAULT ((-1)) FOR [userid],CONSTRAINT [DF_dnt_online_ip] DEFAULT ('0.0.0.0') FOR [ip],CONSTRAINT [DF_dnt_online_username] DEFAULT ('') FOR [username],CONSTRAINT [DF_dnt_online_nickname] DEFAULT ('') FOR [nickname],CONSTRAINT [DF_dnt_online_password] DEFAULT ('') FOR [password],CONSTRAINT [DF_dnt_online_groupid] DEFAULT (0) FOR [groupid],CONSTRAINT [DF_dnt_online_olimg] DEFAULT ('') FOR [olimg],CONSTRAINT [DF_dnt_online_adminid] DEFAULT (0) FOR [adminid],CONSTRAINT [DF_dnt_online_invisible] DEFAULT (0) FOR [invisible],CONSTRAINT [DF_dnt_online_action] DEFAULT (0) FOR [action],CONSTRAINT [DF_dnt_online_lastactivity] DEFAULT (0) FOR [lastactivity],CONSTRAINT [DF_dnt_online_lastposttime] DEFAULT ('1900-1-1 00:00:00') FOR [lastposttime],CONSTRAINT [DF_dnt_online_lastpostpmtime] DEFAULT ('1900-1-1 00:00:00') FOR [lastpostpmtime],CONSTRAINT [DF_dnt_online_lastsearchtime] DEFAULT ('1900-1-1 00:00:00') FOR [lastsearchtime],CONSTRAINT [DF_dnt_online_lastupdatetime] DEFAULT (getdate()) FOR [lastupdatetime],CONSTRAINT [DF_dnt_online_forumid] DEFAULT (0) FOR [forumid],CONSTRAINT [DF_dnt_online_forumname] DEFAULT ('') FOR [forumname],CONSTRAINT [DF_dnt_online_titleid] DEFAULT (0) FOR [titleid],CONSTRAINT [DF_dnt_online_title] DEFAULT ('') FOR [title],CONSTRAINT [DF_dnt_online_verifycode] DEFAULT ('') FOR [verifycode];");
11sb.Append("CREATE INDEX [forum] ON [dnt_online]([userid], [forumid], [invisible]) ON [PRIMARY];");
12sb.Append("CREATE INDEX [invisible] ON [dnt_online]([userid], [invisible]) ON [PRIMARY];");
13sb.Append("CREATE INDEX [forumid] ON [dnt_online]([forumid]) ON [PRIMARY];");
14sb.Append("CREATE INDEX [password] ON [dnt_online]([userid], [password]) ON [PRIMARY];");
15sb.Append("CREATE INDEX [ip] ON [dnt_online]([userid], [ip]) ON [PRIMARY];");
16
17return DbHelper.ExecuteNonQuery(CommandType.Text, sb.Replace("dnt_", BaseConfigs.GetBaseConfig().Tableprefix).ToString());
18}
19catch
20{
21return -1;
22}
23}
该方法如果正常运行的话,会在数据库中建立dnt_onlines"这个表。用于记录在线用户的全部信息。上文介绍的仅仅是系统“初始化”时所要做的“活”。而更重要的内容是用户从一访问论坛并执行一系列操作(如登陆,发贴,浏览版块等)时,“在线用户”机制在里面所启的重要作用。这里先以登陆(系统)这一操作发生时,程序所做出的响应为例,看一下在线这块是如何进行绑定的,请看如下代码(位于discuz.web项目的“aspx/1/”文件夹下的login.aspx.cs文件):
OnlineUsers.UpdateAction(olid, UserAction.Login.ActionID, 0, config.Onlinetimeout);
它的作用就是将已成功登陆系统的用户所执行的当前动作及相关信息更新到"dnt_onlines" 表中。而这个函数本身所用到的四个参数要重点介绍一下:
olid: 在线列表id,对应"dnt_onlines"表中的olid字段
action:动作(结构类型,详情参见discuz.forum项目下的forumutils.cs文件)用于传递动作的相关信息如:ActionName动作名称和ActionDescription动作描述等)
inid: 所在位置代码,即当前用户所访问的主题(TopicId)。因为登陆不牵扯主题操作,所以上面的值为0
timeout: 无动作离线时间(config.Onlinetimeout),这个数据是在后台进行设置的,
timeout的作用就是当有别人访问论坛时,在更新自身在线状态信息同时,用这个设置数据(整型)与在线表(dnt_onlines)中的"lastupdatetime"字段(之后活动时间)进行比较,找出超过
timeout规定的时间的用户,将其在线状态(onlinestate)设置为0(即为离线)。而做这件事的函数就是在项目“discuz.data.sqlserver”下的“UserManage.cs”中的“AddOnlineUser”函数,这里将它的代码贴出来,详情见注释:
1/// <summary>
2/// 执行在线用户向表及缓存中添加的操作。
3/// </summary>
4/// <param name="__onlineuserinfo">在组用户信息内容</param>
5/// <returns>添加成功则返回刚刚添加的olid,失败则返回0</returns>
6public int AddOnlineUser(OnlineUserInfo __onlineuserinfo, int timeout)
7{
8
9string strDelTimeOutSql = "";
10// 此处的设置见后台forum_uisetting.aspx.cs源码
11// 如果timeout为负数则代表不需要精确更新用户是否在线的状态
12if (timeout > 0)
13{
14if (__onlineuserinfo.Userid > 0)
15{
16strDelTimeOutSql = string.Format("{0}UPDATE [{1}users] SET [onlinestate]=1 WHERE [uid]={2};", strDelTimeOutSql, BaseConfigs.GetTablePrefix, __onlineuserinfo.Userid.ToString());
17}
18}
19else
20{
21timeout = timeout * -1;
22}
23
24if (timeout > 9999)
25{
26timeout = 9999;
27}
28
29System.Text.StringBuilder sb = new System.Text.StringBuilder();
30System.Text.StringBuilder sb2 = new System.Text.StringBuilder();
31
32IDataReader dr = DbHelper.ExecuteReader(CommandType.Text, string.Format("SELECT [userid] FROM [{0}online] WHERE [lastupdatetime]<'{1}'", BaseConfigs.GetTablePrefix, DateTime.Parse(DateTime.Now.AddMinutes(timeout * -1).ToString("yyyy-MM-dd HH:mm:ss"))));
33try
34{
35while (dr.Read())
36{
37sb.Append(",");
38sb.Append(dr[0].ToString());
39if (dr[0].ToString() != "-1")
40{
41sb2.Append(",");
42sb2.Append(dr[0].ToString());
43}
44}
45}
46finally
47{
48dr.Close();
49}
50
51if (sb.Length > 0)
52{
53sb.Remove(0, 1);
54strDelTimeOutSql = string.Format("{0}DELETE FROM [{1}online] WHERE [userid] IN ({2});", strDelTimeOutSql, BaseConfigs.GetTablePrefix, sb.ToString());
55}
56if (sb2.Length > 0)
57{
58sb2.Remove(0, 1);
59strDelTimeOutSql = string.Format("{0}UPDATE [{1}users] SET [onlinestate]=0,[lastactivity]=GETDATE() WHERE [uid] IN ({2});", strDelTimeOutSql, BaseConfigs.GetTablePrefix, sb2.ToString());
60}
61
62
63DbParameter[] prams = {};
64int olid = Utils.StrToInt(DbHelper.ExecuteScalar(CommandType.Text, strDelTimeOutSql + "INSERT INTO [" + BaseConfigs.GetTablePrefix + "online] ([userid],[ip],[username],[nickname],[password],[groupid],[olimg],[adminid],[invisible],[action],[lastactivity],[lastposttime],[lastpostpmtime],[lastsearchtime],[lastupdatetime],[forumid],[forumname],[titleid],[title], [verifycode])VALUES(@userid,@ip,@username,@nickname,@password,@groupid,@olimg,@adminid,@invisible,@action,@lastactivity,@lastposttime,@lastpostpmtime,@lastsearchtime,@lastupdatetime,@forumid,@forumname,@titleid,@title,@verifycode);SELECT SCOPE_IDENTITY()", prams).ToString(), 0);
65
66// 如果id值太大则重建在线表
67if (olid > 2147483000)
68{
69CreateOnlineTable();
70DbHelper.ExecuteNonQuery(CommandType.Text, strDelTimeOutSql + "INSERT INTO [" + BaseConfigs.GetTablePrefix + "online] ([userid],[ip],[username],[nickname],[password],[groupid],[olimg],[adminid],[invisible],[action],[lastactivity],[lastposttime],[lastpostpmtime],[lastsearchtime],[lastupdatetime],[forumid],[titleid],[verifycode])VALUES(@userid,@ip,@username,@nickname,@password,@groupid,@olimg,@adminid,@invisible,@action,@lastactivity,@lastposttime,@lastpostpmtime,@lastsearchtime,@lastupdatetime,@forumid,@forumname,@titleid,@title,@verifycode);SELECT SCOPE_IDENTITY()", prams);
71return 1;
72}
75return 0;
76//return (int)DbHelper.ExecuteDataset(CommandType.Text, "SELECT [olid] FROM ["+BaseConfigFactory.GetTablePrefix+"online] WHERE [userid]=" + __onlineuserinfo.Userid.ToString()).Tables[0].Rows[0][0];
77
78}
需要说明一下,就是在线表的删除问题。因为这个在线表是通过程序在数据库中进行创建的,同时表中的olid又是一个自增字段(标识自增为1),因此为了避免自增字段最终超过较大值范围(因为在线表经常有数据添加进来)。所以在上面加入了"olid > 2147483000"的逻辑判断,来预防这个问题的出现。
说了一大堆的关于timeout参数的问题,而另一个重要的参数olid还没作详细介绍呢:)上面所述的登陆页面(login.aspx.cs)中,olid是从basepage类中获得的,而basepage就是前台主要页面的“基类”,它里面的构造函数部分封装了页面中大部分公共变量的初始化操作。所以要了解olid,还要从basepage.cs中分析一把。请看下面的代码段(摘自discuz.web.ui项目下的basepage.cs文件):
1/// <summary>
2/// 当前用户的在线表ID
3/// </summary>
4protected internal int olid;
5
6public BasePage() //构造函数
7{
8
9oluserinfo = OnlineUsers.UpdateInfo(config.Passwordkey, config.Onlinetimeout);
10
11//password 可用于下面的userkey赋值,以实现用户退出操作时的识别认证
12password = oluserinfo.Password;
13if (password.Length > 16)
14{
15userkey = password.Substring(4, 8).Trim();
16}
17else
18{
19userkey = "";
20}
21
22
23olid = oluserinfo.Olid;
24
25}
从代码中可以看出olid是通过louserinfo对象进行赋值的,而louserinfo对象是通过UpdateInfo来进行实始化绑定的,所以我们还需要再看一下这个函数,相关代码段如下:
1/// <summary>
2/// 用户在线信息维护。判断当前用户的身份(会员还是游客),是否在在线列表中存在,如果存在则更新会员的当前动,不存在则建立.
3/// </summary>
4/// <param name="passwordkey">论坛passwordkey</param>
5/// <param name="timeout">在线超时时间</param>
6/// <param name="passwd">用户密码</param>
7public static OnlineUserInfo UpdateInfo(string passwordkey, int timeout, int uid, string passwd)
8{
9
10 lock(SynObject)
11 {
12OnlineUserInfo __onlineuser = new OnlineUserInfo();
13
14string ip = DNTRequest.GetIP();
15int userid = Utils.StrToInt(ForumUtils.GetCookie("userid"), uid);
16string password = (passwd == string.Empty ? ForumUtils.GetCookiePassword(passwordkey) : ForumUtils.GetCookiePassword(passwd,passwordkey));
17
18if (password.Length == 0)
19{
20 userid = -1;
21}
22 // 如果密码非Base64编码字符串则怀疑被非法篡改, 直接置身份为游客
23else if (!Utils.IsBase64String(password))
24{
25 userid = -1;
26}
27
28if (userid != -1)
29{
30 __onlineuser = GetOnlineUser(userid,password);
31 if (__onlineuser != null)
32 {
33
34if (__onlineuser.Ip != ip)
35{
36 UpdateIP(__onlineuser.Olid, ip);
37
38 __onlineuser.Ip = ip;
39
40 return __onlineuser;
41}
42 }
43 else
44 {
45
46// 判断密码是否正确
47userid = Users.CheckPassword(userid, password, false);
48if (userid != -1)
49{
50 DeleteRowsByIP(ip);
51 return CreateUser(userid, timeout);
52}
53else
54{
55 // 如密码错误则在在线表中创建游客
56 __onlineuser = GetOnlineUserByIP(-1, ip);
57 if (__onlineuser == null)
58 {
59return CreateGuestUser(timeout);
60 }
61}
62 }
63
64}
65else
66{
67 __onlineuser = GetOnlineUserByIP(-1, ip);
68 if (__onlineuser == null)
69 {
70return CreateGuestUser(timeout);
71 }
72
73}
74
75__onlineuser.Lastupdatetime = Utils.GetDateTime();
76return __onlineuser;
77
78 }
79
80}
看到这里大家明白了吧,原来不管用户是否注册登陆,这个函数都会将一些信息更新或添加到在线表中。即判断当前用户的身份(会员还是游客),是否在"在线列表"中存在,如果存在则更新会员的当前动作,不存在(首次访问)则初始化相关(游客)用户信息。下面再介绍一下action这个参数,它是结构(struct)类型,里面的ActionName动作名称和ActionD-escription动作描述(目前未用上)是用于显示当前动作信息的。如果大家觉得没问题的话,下面再介绍一下注销(退出)操作,因为有登陆就会有退出。而注销这块的操作也很简单,请看如下代码段(位于aspx/1/logout.aspx.cs文件):
1public class logout : BasePage
2{
3protected override void ShowPage()
4{
5
6
7if (DNTRequest.GetString("userkey") == userkey)
8{
9AddMsgLine("已经清除了您的登录信息, 稍后您将以游客身份返回首页");
10Users.UpdateOnlineTime(uid);
11OnlineUsers.DeleteRows(olid);
12ForumUtils.ClearUserCookie();
13Utils.WriteCookie(Utils.GetTemplateCookieName(), "", -999999);
14}
15else
16{
17AddMsgLine("无法确定您的身份, 稍后返回首页");
18}
19}
20}
21
22
代码中的userkey其实是一个用户password的一个子串(详情上面basepage.cs代码中的相应部分)。而“OnlineUsers.DeleteRows(olid);”这行代码其实就是删除在线表中指定olid的用户信息了。因
为代码过于简单,只是一条SQL语句而已,所以就不在这里多费笔默了。
其实关于在线表有关的操作还有很多,大家不妨挖一下onlines.cs这个文件,从中会有更多的收获。
之后再介绍一下有关“较大在线人数”这个问题,其实这个功能主要是为了中小站长(服务器资源相对紧张)提供的一项设置,旨在当用户在线人数达到一定数量时,拒绝其它的访问。相应的代码判断逻辑如下(摘自basepage.cs文件):
1 if (config.Onlinetimeout > 0 && userid != -1)
2 {
3onlineusercount = OnlineUsers.GetOnlineAllUserCount();
4 }
5else
6 {
7onlineusercount = OnlineUsers.GetCacheOnlineAllUserCount();
8 }
9if (onlineusercount >= config.Maxonlines && useradminid != 1 && pagename != "login.aspx" && pagename != "logout.aspx")
10 {
11ShowMessage("抱歉,目前访问人数太多,你暂时无法访问论坛.", 0);
12return;
13 }
14
里面的GetOnlineAllUserCount()和GetCacheOnlineAllUserCount()均为返回在线人数的方法,而config.Onlinetimeout的设置是在管理后 成的。
本文标签:
很赞哦! ()
相关教程
图文教程
Discuz网站模版制作方法
页头文件路径:template/default/common/header.htmheader.htm文件二次加载的文件有template/default/common/header_common.htm 全局公共文件
discuz x1二次开发文件目录介绍
|--admin.php管理员入口|--api.php接口文件|--category.php分类入口|--cp.php个人资料设置入口|--crossdomain.xmlFLASH跨域传输文件|--favicon.ico系统icon图标
Discuz3.X整合CAS的方法示例
有很多朋友问我为什么没能整合成功?我也没有能及时回复,在此表示抱歉。实际上将登录的过程是在Cas Server 登录页面 中完成的, DZ中的登录用户名密码这些都可以隐藏掉。 这个需要改模板了。
mysql+apache+Discuz搭建Discuz论坛
把1.2创建的数据库分配给1.1创建的用户。(之所以这样做,是为了防止数据库被注入时,只能操作本数据库,而不能操作数据库的user表,该表存有数据库的用户名和密码
相关源码
-
(自适应)帝国cms7.5模板自媒体文章新闻博客为帝国CMS7.5设计的响应式模板,采用H5技术构建现代化内容展示框架。通过智能断点检测技术实现手机、平板、PC三端适配查看源码 -
(自适应)帝国cms7.5文章新闻博客整站源码( 带会员中心)本模板基于帝国CMS内核开发,为新闻资讯、个人博客及作品展示类网站设计。采用响应式布局技术,确保在手机、平板和电脑等不同设备上都能获得良好的浏览体验。查看源码 -
(自适应响应式)html5高档服装定制西服pbootcms模板下载本模板基于PbootCMS内核开发,为服装定制企业和服装品牌量身打造。设计风格时尚现代,充分展现服装行业的审美特质与品牌魅力。采用HTML5响应式技术,确保在各种设备上呈现视觉效果。整站布局注重产品展示与品牌叙事,帮助企业有效展示服装系列与定制服务,提升客户体验。查看源码 -
(带手机版)绿色生态农业种植农产品网站pbootcms源码下载本模板基于PbootCMS内核开发,为生态农业企业量身打造,适用于农业种植、有机农场、农产品电商等业务场景。模板设计充分体现绿色环保理念,突出农业特色,同时保持简洁大气的视觉效果,帮助农业企业建立专业形象。查看源码 -
(自适应响应式)运动健身瑜伽俱乐部网站pbootcms源码下载为健身瑜伽俱乐部设计的响应式网站模板,采用PbootCMS内核开发,可快速搭建专业级企业官网。模板默认适配运动健身行业视觉风格,用户可通过替换图文内容灵活应用于其他服务行业。查看源码 -
(自适应)WordPress主题SEO自媒体博客资讯模板RabbitV2.0Rabbit v2.0主题专注于网站搜索引擎优化需求,为博客、自媒体及资讯类网站提供专业的SEO技术解决方案。该主题从架构设计到功能实现均围绕搜索引擎优化理念展开。查看源码
| 分享笔记 (共有 篇笔记) |
